Compare commits
341 Commits
v0.0.5
...
doughlang-
| Author | SHA1 | Date | |
|---|---|---|---|
| 91025fccb0 | |||
| 55e02fd919 | |||
| 4e4c61ee4f | |||
| 8d641d0060 | |||
| 06ed0eae54 | |||
| e0eb0d5a02 | |||
| d62656c615 | |||
| acc3ed12b3 | |||
| 722036dfbb | |||
| 4a2cd9303e | |||
| 35edfdbb17 | |||
| b54826cdd5 | |||
| 6b24980fc7 | |||
| 55324af358 | |||
| df9973b119 | |||
| f41e5fc49a | |||
| ead1f351a7 | |||
| 986bac9e6b | |||
| 02239c5ce4 | |||
| 1f9d32f972 | |||
| 8dd2920fca | |||
| df6089f84a | |||
| 62940b3d24 | |||
| 1fe796dda7 | |||
| f0c871711c | |||
| fcab20579a | |||
| 239785b322 | |||
| 259c9f8bb6 | |||
| c9ffeaddce | |||
| 12daf35c07 | |||
| 6a0607b93a | |||
| 4f40bd4f99 | |||
| 2f94ddd23f | |||
| 0f9044bb3e | |||
| 8732cca3f9 | |||
| 74220d3bff | |||
| 8b0a122dfc | |||
| e165e029dc | |||
| 148ef34a01 | |||
| 6ba62ac1c4 | |||
| ae026420f1 | |||
| d80f2f6315 | |||
| 89ed9b2a39 | |||
| 47608668fa | |||
| 6ce27c522f | |||
| 233e4dab4e | |||
| f95c6ee239 | |||
| 964917d2f0 | |||
| 6bb855cff7 | |||
| 124bb2f680 | |||
| 08b5937fc2 | |||
| ccfa4c7723 | |||
| d3e20e53d0 | |||
| e08bf57dc1 | |||
| a5590168ee | |||
| 3e2063835b | |||
| e6156343c3 | |||
| 6c6d2d04a7 | |||
| a023551d9f | |||
| dc1c9bdd6d | |||
| c5e817f1e5 | |||
| 6108d66b0a | |||
| 09fdb14d79 | |||
| 4228324ab3 | |||
| f5f905cd70 | |||
| 883387aaf1 | |||
| 2d706ff582 | |||
| cd2e3c3e32 | |||
| 8c23aea4af | |||
| d6c0a6cf1b | |||
| fc80be5fcc | |||
| 7c2dd1468b | |||
| 4747b65414 | |||
| 8ff17fd475 | |||
| 681fbc88d3 | |||
| 7cf485fade | |||
| 3b96833fcb | |||
| 65b75f95ce | |||
| 4c4b49ce00 | |||
| 7ba808594c | |||
| ef92d8b798 | |||
| 8c8f1254e0 | |||
| 82e62ab4ac | |||
| b09a610c6c | |||
| fa5244dcf9 | |||
| 9b460afed4 | |||
| 27d1d07ed8 | |||
| 68e676eda4 | |||
| c988193049 | |||
| 2ecb2efc09 | |||
| a4176c710e | |||
| cdb9ec49fe | |||
| 6d33c4baa9 | |||
| 33e13425a9 | |||
| 11c8daaed0 | |||
| 584207fc8c | |||
| dcdb100a8a | |||
| fdf076c272 | |||
| 2fc847fff2 | |||
| 4bc088f277 | |||
| 06bcb6b7c6 | |||
| 7e311cb0ef | |||
| c0ad544486 | |||
| fd54b513be | |||
| adbabc66c5 | |||
| 5c99bf09ab | |||
| 4d9b13f7a1 | |||
| d9ac9e628d | |||
| 632ddf0eab | |||
| e39b390441 | |||
| 2fd08193fd | |||
| 7d3f189100 | |||
| cc6168b55e | |||
| e3d94d8949 | |||
| 7a8da33de9 | |||
| 697d139cfd | |||
| 5d2c714bc1 | |||
| b115fea71b | |||
| eebabf02fb | |||
| 088cd4d1e4 | |||
| 0fd9c002fc | |||
| 772286eefa | |||
| 3b14186b70 | |||
| a6ad20911d | |||
| 01cf9d93e2 | |||
| edabbe1655 | |||
| af9c293907 | |||
| 0e3ba342c4 | |||
| d95d35268e | |||
| 0c2b0002ce | |||
| 3534be5fbc | |||
| 026681787a | |||
| 80e1219808 | |||
| 6ee9bbd72e | |||
| 6e94b702c9 | |||
| d21683ad61 | |||
| 518fbe74a1 | |||
| bc955c6409 | |||
| 678c0f952c | |||
| 86c4da0689 | |||
| 5db77db6b8 | |||
| 145a24c5ff | |||
| 485afb7843 | |||
| 01871bf455 | |||
| fd361f2bea | |||
| 0eef6b910c | |||
| c50940a44c | |||
| 3cda3d83d9 | |||
| e5a51ba6c2 | |||
| 883fd31d38 | |||
| d71276b477 | |||
| d8e32ee263 | |||
| e419c23769 | |||
| 1bd9c021dd | |||
| df68d6f2e6 | |||
| ae11d87d68 | |||
| 96be5aba6c | |||
| b9f4994930 | |||
| f4fe07a08b | |||
| 94be5d787f | |||
| 5deb585054 | |||
| 56e71d6782 | |||
| c62df3d8b3 | |||
| fad28beb05 | |||
| 0f8b0824ac | |||
| 99a00875a8 | |||
| 8675f91aca | |||
| de63a8c123 | |||
| 533436afc1 | |||
| 1eb0516baf | |||
| 97808fd855 | |||
| 388a69948e | |||
| 5e7ba6de24 | |||
| adb0fd229c | |||
| 0e545077c6 | |||
| b64cc232f9 | |||
| b0341f06fd | |||
| a3e383b53f | |||
| 1b217b2e75 | |||
| 5662bd8524 | |||
| 28f9048087 | |||
| b17164b68b | |||
| ecebefe218 | |||
| fc374e0108 | |||
| 4295982876 | |||
| 729155d3a4 | |||
| 8c0ae02a71 | |||
| 7f7836877e | |||
| b2733aa171 | |||
| a233bb18bc | |||
| e06a27a5b1 | |||
| 3f5c5480ae | |||
| 53cf71608a | |||
| 883c2677d9 | |||
| 7d98ef87d5 | |||
| a188c5b65e | |||
| 872818fe7c | |||
| 3aef055739 | |||
| 38a5d31b08 | |||
| e43847bbd4 | |||
| a8b8a91c79 | |||
| 695c812bf5 | |||
| 524c84be9e | |||
| 4096442f75 | |||
| 03a4e76292 | |||
| 46a1639990 | |||
| 5ea8039a8a | |||
| 479efbad73 | |||
| a462dd2be3 | |||
| 4d6b94b570 | |||
| fe2b816f27 | |||
| e19127facc | |||
| b7ad285a11 | |||
| 61d8cf8550 | |||
| 70872d86f9 | |||
| 6bf34fdff6 | |||
| 9d7ab77999 | |||
| 82b71e2517 | |||
| 46bd44bd99 | |||
| 3511575669 | |||
| b3d62c09aa | |||
| ded100bf71 | |||
| c9ddebb946 | |||
| 15c4b89bce | |||
| aa7612926e | |||
| fffc370380 | |||
| a646a9e521 | |||
| 5f57924f23 | |||
| d692f6bb80 | |||
| 58c5a01312 | |||
| 16baaa32f1 | |||
| 3c4d31c473 | |||
| d723f7cece | |||
| b446677eda | |||
| 0beb121f32 | |||
| 6b16c55d97 | |||
| c16dbca55c | |||
| 4c883d87a4 | |||
| 1c3a56f5b5 | |||
| 406bfb8882 | |||
| e0f54aea97 | |||
| fa8a71addc | |||
| 0cc0cb5cfb | |||
| f330a7eaa5 | |||
| 8d8928b8a8 | |||
| a033e9f33b | |||
| be81221895 | |||
| 33b7cd3971 | |||
| c9266d971f | |||
| f76756e0e4 | |||
| a89f45aa58 | |||
| d2eb165759 | |||
| edf175e53b | |||
| 6aea23c8ba | |||
| db0b791b24 | |||
| 7c73fd335c | |||
| d7ce33e457 | |||
| 0d937728ed | |||
| a8ef989084 | |||
| e7c5a02afa | |||
| 12046fa9f7 | |||
| fb7de717d0 | |||
| 3fe5916a4f | |||
| 2c57f848ea | |||
| 81cf05cc69 | |||
| 83423f37be | |||
| ecf97801d6 | |||
| 71745161c4 | |||
| 9566f098ac | |||
| b9085551e1 | |||
| a877c0d726 | |||
| 893b716c86 | |||
| e49b171bea | |||
| 901e9d1d5b | |||
| aa3f357fca | |||
| d4432cda7a | |||
| 40ec9b30e4 | |||
| ede00c3c86 | |||
| be604b7b45 | |||
| e70ffd1895 | |||
| f24bd10c53 | |||
| 8453b092f1 | |||
| 42307d2ab4 | |||
| 45d75bb552 | |||
| b74c4cd5bf | |||
| 0c518b47e6 | |||
| 169f61144b | |||
| a3a87e0b67 | |||
| ed9b73a1a3 | |||
| 9b11543396 | |||
| 2ed8481489 | |||
| a3bb1ef447 | |||
| f483d690e2 | |||
| 087969e117 | |||
| 116d98437c | |||
| 8121c1c8bb | |||
| 2a5e965edf | |||
| bf16338166 | |||
| 9449e5ba06 | |||
| b796411742 | |||
| ef190f2d66 | |||
| 9c3c2e8674 | |||
| 02323ae6f2 | |||
| e36a684422 | |||
| 5341631781 | |||
| efd442bbfa | |||
| 9dc0cc7841 | |||
| 90a3818ca0 | |||
| 2a62a1c714 | |||
| 01ffdb67a6 | |||
| de024b6cb7 | |||
| 2834e4a8ea | |||
| 4ff101f0ee | |||
| 1fa027a0c2 | |||
| 9a687624fc | |||
| e102ae25b4 | |||
| a56ee38b15 | |||
| f315fb5af7 | |||
| e4f270da17 | |||
| 17a522b633 | |||
| 736fc37a81 | |||
| 02b775259e | |||
| 00d72b823a | |||
| ec1a1255ad | |||
| 0e8b4f68c3 | |||
| eee9e99aed | |||
| f6e44f3773 | |||
| 9e90eea7b6 | |||
| 83694988c3 | |||
| 98868d3960 | |||
| 75adbd6473 | |||
| d0ed8309f4 | |||
| 0fab11c11b | |||
| f958bbcb79 | |||
| d07a3e1455 | |||
| 489a1f7944 | |||
| bc33b60265 | |||
| 89cd1393ed | |||
| 3bebac6798 | |||
| 6ea99fc6f5 | |||
| 6589376870 |
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[registries.soft-fish]
|
||||||
|
index = "https://git.soft.fish/j/_cargo-index.git"
|
||||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -1,3 +1,13 @@
|
|||||||
|
|
||||||
|
# Visual Studio Code config
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
# Rust
|
||||||
**/Cargo.lock
|
**/Cargo.lock
|
||||||
target
|
target
|
||||||
|
|
||||||
|
# Symbol table dump?
|
||||||
|
typeck-table.ron
|
||||||
|
|
||||||
|
# Pest files generated by Grammatical
|
||||||
*.p*st
|
*.p*st
|
||||||
|
|||||||
24
Cargo.toml
24
Cargo.toml
@@ -1,20 +1,24 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"cl-repl",
|
"compiler/cl-repl",
|
||||||
"cl-typeck",
|
"compiler/cl-typeck",
|
||||||
"cl-interpret",
|
"compiler/cl-embed",
|
||||||
"cl-structures",
|
"compiler/cl-interpret",
|
||||||
"cl-token",
|
"compiler/cl-structures",
|
||||||
"cl-ast",
|
"compiler/cl-token",
|
||||||
"cl-parser",
|
"compiler/cl-ast",
|
||||||
"cl-lexer",
|
"compiler/cl-parser",
|
||||||
|
"compiler/cl-lexer",
|
||||||
]
|
]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
repository = "https://git.soft.fish/j/Conlang"
|
repository = "https://git.soft.fish/j/Conlang"
|
||||||
version = "0.0.5"
|
version = "0.0.10"
|
||||||
authors = ["John Breaux <j@soft.fish>"]
|
authors = ["John Breaux <j@soft.fish>"]
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
publish = ["soft-fish"]
|
publish = ["soft-fish"]
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
opt-level = 1
|
||||||
|
|||||||
@@ -1,643 +0,0 @@
|
|||||||
//! Implementations of AST nodes and traits
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
mod display {
|
|
||||||
//! Implements [Display] for [AST](super::super) Types
|
|
||||||
use super::*;
|
|
||||||
pub use delimiters::*;
|
|
||||||
use std::{
|
|
||||||
borrow::Borrow,
|
|
||||||
fmt::{Display, Write},
|
|
||||||
};
|
|
||||||
mod delimiters {
|
|
||||||
#![allow(dead_code)]
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct Delimiters<'t> {
|
|
||||||
pub open: &'t str,
|
|
||||||
pub close: &'t str,
|
|
||||||
}
|
|
||||||
/// Delimits with braces decorated with spaces `" {n"`, ..., `"\n}"`
|
|
||||||
pub const SPACED_BRACES: Delimiters = Delimiters { open: " {\n", close: "\n}" };
|
|
||||||
/// Delimits with braces on separate lines `{\n`, ..., `\n}`
|
|
||||||
pub const BRACES: Delimiters = Delimiters { open: "{\n", close: "\n}" };
|
|
||||||
/// Delimits with parentheses on separate lines `{\n`, ..., `\n}`
|
|
||||||
pub const PARENS: Delimiters = Delimiters { open: "(\n", close: "\n)" };
|
|
||||||
/// Delimits with square brackets on separate lines `{\n`, ..., `\n}`
|
|
||||||
pub const SQUARE: Delimiters = Delimiters { open: "[\n", close: "\n]" };
|
|
||||||
/// Delimits with braces on the same line `{ `, ..., ` }`
|
|
||||||
pub const INLINE_BRACES: Delimiters = Delimiters { open: "{ ", close: " }" };
|
|
||||||
/// Delimits with parentheses on the same line `( `, ..., ` )`
|
|
||||||
pub const INLINE_PARENS: Delimiters = Delimiters { open: "(", close: ")" };
|
|
||||||
/// Delimits with square brackets on the same line `[ `, ..., ` ]`
|
|
||||||
pub const INLINE_SQUARE: Delimiters = Delimiters { open: "[", close: "]" };
|
|
||||||
}
|
|
||||||
fn delimit<'a>(
|
|
||||||
func: impl Fn(&mut std::fmt::Formatter<'_>) -> std::fmt::Result + 'a,
|
|
||||||
delim: Delimiters<'a>,
|
|
||||||
) -> impl Fn(&mut std::fmt::Formatter<'_>) -> std::fmt::Result + 'a {
|
|
||||||
move |f| {
|
|
||||||
write!(f, "{}", delim.open)?;
|
|
||||||
func(f)?;
|
|
||||||
write!(f, "{}", delim.close)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn separate<'iterable, I>(
|
|
||||||
iterable: &'iterable [I],
|
|
||||||
sep: impl Display + 'iterable,
|
|
||||||
) -> impl Fn(&mut std::fmt::Formatter<'_>) -> std::fmt::Result + 'iterable
|
|
||||||
where
|
|
||||||
I: Display,
|
|
||||||
{
|
|
||||||
move |f| {
|
|
||||||
for (idx, item) in iterable.iter().enumerate() {
|
|
||||||
if idx > 0 {
|
|
||||||
write!(f, "{sep}")?;
|
|
||||||
}
|
|
||||||
item.fmt(f)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Mutability {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Mutability::Not => Ok(()),
|
|
||||||
Mutability::Mut => "mut ".fmt(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Visibility {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Visibility::Private => Ok(()),
|
|
||||||
Visibility::Public => "pub ".fmt(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for File {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
separate(&self.items, "\n")(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Attrs {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { meta } = self;
|
|
||||||
if meta.is_empty() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
"#".fmt(f)?;
|
|
||||||
delimit(separate(meta, ", "), INLINE_SQUARE)(f)?;
|
|
||||||
"\n".fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Meta {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { name, kind } = self;
|
|
||||||
write!(f, "{name}{kind}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for MetaKind {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
MetaKind::Plain => Ok(()),
|
|
||||||
MetaKind::Equals(v) => write!(f, " = {v}"),
|
|
||||||
MetaKind::Func(args) => delimit(separate(args, ", "), INLINE_PARENS)(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Item {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { extents: _, attrs, vis, kind } = self;
|
|
||||||
attrs.fmt(f)?;
|
|
||||||
vis.fmt(f)?;
|
|
||||||
match kind {
|
|
||||||
ItemKind::Alias(v) => v.fmt(f),
|
|
||||||
ItemKind::Const(v) => v.fmt(f),
|
|
||||||
ItemKind::Static(v) => v.fmt(f),
|
|
||||||
ItemKind::Module(v) => v.fmt(f),
|
|
||||||
ItemKind::Function(v) => v.fmt(f),
|
|
||||||
ItemKind::Struct(v) => v.fmt(f),
|
|
||||||
ItemKind::Enum(v) => v.fmt(f),
|
|
||||||
ItemKind::Impl(v) => v.fmt(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Alias {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { to, from } = self;
|
|
||||||
match from {
|
|
||||||
Some(from) => write!(f, "type {to} = {from};"),
|
|
||||||
None => write!(f, "type {to};"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Const {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { name, ty, init } = self;
|
|
||||||
write!(f, "const {name}: {ty} = {init}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Static {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { mutable, name, ty, init } = self;
|
|
||||||
write!(f, "static {mutable}{name}: {ty} = {init}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Module {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { name, kind } = self;
|
|
||||||
write!(f, "mod {name}{kind}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for ModuleKind {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
ModuleKind::Inline(items) => {
|
|
||||||
' '.fmt(f)?;
|
|
||||||
delimit(|f| items.fmt(f), BRACES)(f)
|
|
||||||
}
|
|
||||||
ModuleKind::Outline => ';'.fmt(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Function {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { name, args, body, rety } = self;
|
|
||||||
write!(f, "fn {name} ")?;
|
|
||||||
delimit(separate(args, ", "), INLINE_PARENS)(f)?;
|
|
||||||
if let Some(rety) = rety {
|
|
||||||
write!(f, " -> {rety}")?;
|
|
||||||
}
|
|
||||||
match body {
|
|
||||||
Some(body) => write!(f, " {body}"),
|
|
||||||
None => ';'.fmt(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Param {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { mutability, name, ty } = self;
|
|
||||||
write!(f, "{mutability}{name}: {ty}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Struct {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { name, kind } = self;
|
|
||||||
write!(f, "struct {name}{kind}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for StructKind {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
StructKind::Empty => ';'.fmt(f),
|
|
||||||
StructKind::Tuple(v) => delimit(separate(v, ", "), INLINE_PARENS)(f),
|
|
||||||
StructKind::Struct(v) => delimit(separate(v, ",\n"), SPACED_BRACES)(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for StructMember {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { vis, name, ty } = self;
|
|
||||||
write!(f, "{vis}{name}: {ty}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Enum {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { name, kind } = self;
|
|
||||||
write!(f, "enum {name}{kind}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for EnumKind {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
EnumKind::NoVariants => ';'.fmt(f),
|
|
||||||
EnumKind::Variants(v) => delimit(separate(v, ",\n"), SPACED_BRACES)(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Variant {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { name, kind } = self;
|
|
||||||
write!(f, "{name}{kind}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for VariantKind {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
VariantKind::Plain => Ok(()),
|
|
||||||
VariantKind::CLike(n) => write!(f, " = {n}"),
|
|
||||||
VariantKind::Tuple(v) => delimit(separate(v, ", "), INLINE_PARENS)(f),
|
|
||||||
VariantKind::Struct(v) => delimit(separate(v, ", "), INLINE_BRACES)(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Impl {
|
|
||||||
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
todo!("impl Display for Impl")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Ty {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match &self.kind {
|
|
||||||
TyKind::Never => "!".fmt(f),
|
|
||||||
TyKind::Empty => "()".fmt(f),
|
|
||||||
TyKind::SelfTy => "Self".fmt(f),
|
|
||||||
TyKind::Path(v) => v.fmt(f),
|
|
||||||
TyKind::Tuple(v) => v.fmt(f),
|
|
||||||
TyKind::Ref(v) => v.fmt(f),
|
|
||||||
TyKind::Fn(v) => v.fmt(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for TyTuple {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
delimit(separate(&self.types, ", "), INLINE_PARENS)(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for TyRef {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { count: _, to } = self;
|
|
||||||
for _ in 0..self.count {
|
|
||||||
f.write_char('&')?;
|
|
||||||
}
|
|
||||||
write!(f, "{to}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for TyFn {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { args, rety } = self;
|
|
||||||
write!(f, "fn {args}")?;
|
|
||||||
match rety {
|
|
||||||
Some(v) => write!(f, " -> {v}"),
|
|
||||||
None => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Stmt {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Stmt { extents: _, kind, semi } = self;
|
|
||||||
match kind {
|
|
||||||
StmtKind::Empty => Ok(()),
|
|
||||||
StmtKind::Local(v) => v.fmt(f),
|
|
||||||
StmtKind::Item(v) => v.fmt(f),
|
|
||||||
StmtKind::Expr(v) => v.fmt(f),
|
|
||||||
}?;
|
|
||||||
match semi {
|
|
||||||
Semi::Terminated => ';'.fmt(f),
|
|
||||||
Semi::Unterminated => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Let {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { mutable, name, ty, init } = self;
|
|
||||||
write!(f, "let {mutable}{name}")?;
|
|
||||||
if let Some(value) = ty {
|
|
||||||
write!(f, ": {value}")?;
|
|
||||||
}
|
|
||||||
if let Some(value) = init {
|
|
||||||
write!(f, " = {value}")?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Expr {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
self.kind.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for ExprKind {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
ExprKind::Assign(v) => v.fmt(f),
|
|
||||||
ExprKind::Binary(v) => v.fmt(f),
|
|
||||||
ExprKind::Unary(v) => v.fmt(f),
|
|
||||||
ExprKind::Index(v) => v.fmt(f),
|
|
||||||
ExprKind::Path(v) => v.fmt(f),
|
|
||||||
ExprKind::Literal(v) => v.fmt(f),
|
|
||||||
ExprKind::Array(v) => v.fmt(f),
|
|
||||||
ExprKind::ArrayRep(v) => v.fmt(f),
|
|
||||||
ExprKind::AddrOf(v) => v.fmt(f),
|
|
||||||
ExprKind::Block(v) => v.fmt(f),
|
|
||||||
ExprKind::Empty => "()".fmt(f),
|
|
||||||
ExprKind::Group(v) => v.fmt(f),
|
|
||||||
ExprKind::Tuple(v) => v.fmt(f),
|
|
||||||
ExprKind::While(v) => v.fmt(f),
|
|
||||||
ExprKind::If(v) => v.fmt(f),
|
|
||||||
ExprKind::For(v) => v.fmt(f),
|
|
||||||
ExprKind::Break(v) => v.fmt(f),
|
|
||||||
ExprKind::Return(v) => v.fmt(f),
|
|
||||||
ExprKind::Continue(_) => "continue".fmt(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Assign {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { kind, parts } = self;
|
|
||||||
write!(f, "{} {kind} {}", parts.0, parts.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for AssignKind {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
AssignKind::Plain => "=",
|
|
||||||
AssignKind::Mul => "*=",
|
|
||||||
AssignKind::Div => "/=",
|
|
||||||
AssignKind::Rem => "%=",
|
|
||||||
AssignKind::Add => "+=",
|
|
||||||
AssignKind::Sub => "-=",
|
|
||||||
AssignKind::And => "&=",
|
|
||||||
AssignKind::Or => "|=",
|
|
||||||
AssignKind::Xor => "^=",
|
|
||||||
AssignKind::Shl => "<<=",
|
|
||||||
AssignKind::Shr => ">>=",
|
|
||||||
}
|
|
||||||
.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Binary {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { kind, parts } = self;
|
|
||||||
let (head, tail) = parts.borrow();
|
|
||||||
match kind {
|
|
||||||
BinaryKind::Dot => write!(f, "{head}{kind}{tail}"),
|
|
||||||
BinaryKind::Call => write!(f, "{head}{tail}"),
|
|
||||||
_ => write!(f, "{head} {kind} {tail}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for BinaryKind {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
BinaryKind::Lt => "<",
|
|
||||||
BinaryKind::LtEq => "<=",
|
|
||||||
BinaryKind::Equal => "==",
|
|
||||||
BinaryKind::NotEq => "!=",
|
|
||||||
BinaryKind::GtEq => ">=",
|
|
||||||
BinaryKind::Gt => ">",
|
|
||||||
BinaryKind::RangeExc => "..",
|
|
||||||
BinaryKind::RangeInc => "..=",
|
|
||||||
BinaryKind::LogAnd => "&&",
|
|
||||||
BinaryKind::LogOr => "||",
|
|
||||||
BinaryKind::LogXor => "^^",
|
|
||||||
BinaryKind::BitAnd => "&",
|
|
||||||
BinaryKind::BitOr => "|",
|
|
||||||
BinaryKind::BitXor => "^",
|
|
||||||
BinaryKind::Shl => "<<",
|
|
||||||
BinaryKind::Shr => ">>",
|
|
||||||
BinaryKind::Add => "+",
|
|
||||||
BinaryKind::Sub => "-",
|
|
||||||
BinaryKind::Mul => "*",
|
|
||||||
BinaryKind::Div => "/",
|
|
||||||
BinaryKind::Rem => "%",
|
|
||||||
BinaryKind::Dot => ".",
|
|
||||||
BinaryKind::Call => "()",
|
|
||||||
}
|
|
||||||
.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Unary {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { kind, tail } = self;
|
|
||||||
write!(f, "{kind}{tail}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for UnaryKind {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
UnaryKind::Deref => "*",
|
|
||||||
UnaryKind::Neg => "-",
|
|
||||||
UnaryKind::Not => "!",
|
|
||||||
UnaryKind::At => "@",
|
|
||||||
UnaryKind::Tilde => "~",
|
|
||||||
}
|
|
||||||
.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Tuple {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
delimit(separate(&self.exprs, ", "), INLINE_PARENS)(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Index {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { head, indices } = self;
|
|
||||||
write!(f, "{head}")?;
|
|
||||||
for indices in indices {
|
|
||||||
indices.fmt(f)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Path {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { absolute, parts } = self;
|
|
||||||
if *absolute {
|
|
||||||
"::".fmt(f)?;
|
|
||||||
}
|
|
||||||
separate(parts, "::")(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for PathPart {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
PathPart::SuperKw => "super".fmt(f),
|
|
||||||
PathPart::SelfKw => "self".fmt(f),
|
|
||||||
PathPart::Ident(id) => id.fmt(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Identifier {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
self.0.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Literal {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Literal::Bool(v) => v.fmt(f),
|
|
||||||
Literal::Char(v) => write!(f, "'{v}'"),
|
|
||||||
Literal::Int(v) => v.fmt(f),
|
|
||||||
Literal::String(v) => write!(f, "\"{v}\""),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Array {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
delimit(separate(&self.values, ", "), INLINE_SQUARE)(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for ArrayRep {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { value, repeat } = self;
|
|
||||||
write!(f, "[{value}; {repeat}]")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for AddrOf {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { count, mutable, expr } = self;
|
|
||||||
for _ in 0..*count {
|
|
||||||
f.write_char('&')?;
|
|
||||||
}
|
|
||||||
write!(f, "{mutable}{expr}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Block {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
delimit(separate(&self.stmts, "\n"), BRACES)(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Group {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "({})", self.expr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for While {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { cond, pass, fail } = self;
|
|
||||||
write!(f, "while {cond} {pass}{fail}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for If {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { cond, pass, fail } = self;
|
|
||||||
write!(f, "if {cond} {pass}{fail}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for For {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { bind, cond, pass, fail } = self;
|
|
||||||
write!(f, "for {bind} in {cond} {pass}{fail}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Else {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match &self.body {
|
|
||||||
Some(body) => write!(f, " else {body}"),
|
|
||||||
_ => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Break {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "break")?;
|
|
||||||
match &self.body {
|
|
||||||
Some(body) => write!(f, " {body}"),
|
|
||||||
_ => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Return {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "return")?;
|
|
||||||
match &self.body {
|
|
||||||
Some(body) => write!(f, " {body}"),
|
|
||||||
_ => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod convert {
|
|
||||||
//! Converts between major enums and enum variants
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
impl<T: AsRef<str>> From<T> for Identifier {
|
|
||||||
fn from(value: T) -> Self {
|
|
||||||
Identifier(value.as_ref().into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro impl_from ($(impl From for $T:ty {$($from:ty => $to:expr),*$(,)?})*) {$($(
|
|
||||||
impl From<$from> for $T {
|
|
||||||
fn from(value: $from) -> Self {
|
|
||||||
$to(value.into()) // Uses *tuple constructor*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<Box<$from>> for $T {
|
|
||||||
fn from(value: Box<$from>) -> Self {
|
|
||||||
$to((*value).into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*)*}
|
|
||||||
|
|
||||||
impl_from! {
|
|
||||||
impl From for ItemKind {
|
|
||||||
Alias => ItemKind::Alias,
|
|
||||||
Const => ItemKind::Const,
|
|
||||||
Static => ItemKind::Static,
|
|
||||||
Module => ItemKind::Module,
|
|
||||||
Function => ItemKind::Function,
|
|
||||||
Struct => ItemKind::Struct,
|
|
||||||
Enum => ItemKind::Enum,
|
|
||||||
Impl => ItemKind::Impl,
|
|
||||||
}
|
|
||||||
impl From for StructKind {
|
|
||||||
Vec<Ty> => StructKind::Tuple,
|
|
||||||
// TODO: Struct members in struct
|
|
||||||
}
|
|
||||||
impl From for EnumKind {
|
|
||||||
Vec<Variant> => EnumKind::Variants,
|
|
||||||
}
|
|
||||||
impl From for VariantKind {
|
|
||||||
u128 => VariantKind::CLike,
|
|
||||||
Vec<Ty> => VariantKind::Tuple,
|
|
||||||
// TODO: enum struct variants
|
|
||||||
}
|
|
||||||
impl From for TyKind {
|
|
||||||
Path => TyKind::Path,
|
|
||||||
TyTuple => TyKind::Tuple,
|
|
||||||
TyRef => TyKind::Ref,
|
|
||||||
TyFn => TyKind::Fn,
|
|
||||||
}
|
|
||||||
impl From for StmtKind {
|
|
||||||
Let => StmtKind::Local,
|
|
||||||
Item => StmtKind::Item,
|
|
||||||
Expr => StmtKind::Expr,
|
|
||||||
}
|
|
||||||
impl From for ExprKind {
|
|
||||||
Assign => ExprKind::Assign,
|
|
||||||
Binary => ExprKind::Binary,
|
|
||||||
Unary => ExprKind::Unary,
|
|
||||||
Index => ExprKind::Index,
|
|
||||||
Path => ExprKind::Path,
|
|
||||||
Literal => ExprKind::Literal,
|
|
||||||
Array => ExprKind::Array,
|
|
||||||
ArrayRep => ExprKind::ArrayRep,
|
|
||||||
AddrOf => ExprKind::AddrOf,
|
|
||||||
Block => ExprKind::Block,
|
|
||||||
Group => ExprKind::Group,
|
|
||||||
Tuple => ExprKind::Tuple,
|
|
||||||
While => ExprKind::While,
|
|
||||||
If => ExprKind::If,
|
|
||||||
For => ExprKind::For,
|
|
||||||
Break => ExprKind::Break,
|
|
||||||
Return => ExprKind::Return,
|
|
||||||
Continue => ExprKind::Continue,
|
|
||||||
}
|
|
||||||
impl From for Literal {
|
|
||||||
bool => Literal::Bool,
|
|
||||||
char => Literal::Char,
|
|
||||||
u128 => Literal::Int,
|
|
||||||
String => Literal::String,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Option<Expr>> for Else {
|
|
||||||
fn from(value: Option<Expr>) -> Self {
|
|
||||||
Self { body: value.map(Into::into) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<Expr> for Else {
|
|
||||||
fn from(value: Expr) -> Self {
|
|
||||||
Self { body: Some(value.into()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
use std::{
|
|
||||||
fmt::{Result as FmtResult, Write as FmtWrite},
|
|
||||||
io::{Result as IoResult, Write as IoWrite},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Trait which adds a function to [fmt Writers](FmtWrite) to turn them into [Prettifier]
|
|
||||||
pub trait FmtPretty: FmtWrite {
|
|
||||||
/// Indents code according to the number of matched curly braces
|
|
||||||
fn pretty(self) -> Prettifier<'static, Self>
|
|
||||||
where Self: Sized {
|
|
||||||
Prettifier::new(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Trait which adds a function to [io Writers](IoWrite) to turn them into [Prettifier]
|
|
||||||
pub trait IoPretty: IoWrite {
|
|
||||||
/// Indents code according to the number of matched curly braces
|
|
||||||
fn pretty(self) -> Prettifier<'static, Self>
|
|
||||||
where Self: Sized;
|
|
||||||
}
|
|
||||||
impl<W: FmtWrite> FmtPretty for W {}
|
|
||||||
impl<W: IoWrite> IoPretty for W {
|
|
||||||
fn pretty(self) -> Prettifier<'static, Self> {
|
|
||||||
Prettifier::new(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Intercepts calls to either [std::io::Write] or [std::fmt::Write],
|
|
||||||
/// and inserts indentation between matched parentheses
|
|
||||||
pub struct Prettifier<'i, T: ?Sized> {
|
|
||||||
level: isize,
|
|
||||||
indent: &'i str,
|
|
||||||
writer: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'i, W> Prettifier<'i, W> {
|
|
||||||
pub fn new(writer: W) -> Self {
|
|
||||||
Self { level: 0, indent: " ", writer }
|
|
||||||
}
|
|
||||||
pub fn with_indent(indent: &'i str, writer: W) -> Self {
|
|
||||||
Self { level: 0, indent, writer }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'i, W: FmtWrite> Prettifier<'i, W> {
|
|
||||||
#[inline]
|
|
||||||
fn fmt_write_indentation(&mut self) -> FmtResult {
|
|
||||||
let Self { level, indent, writer } = self;
|
|
||||||
for _ in 0..*level {
|
|
||||||
writer.write_str(indent)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'i, W: IoWrite> Prettifier<'i, W> {
|
|
||||||
pub fn io_write_indentation(&mut self) -> IoResult<usize> {
|
|
||||||
let Self { level, indent, writer } = self;
|
|
||||||
let mut count = 0;
|
|
||||||
for _ in 0..*level {
|
|
||||||
count += writer.write(indent.as_bytes())?;
|
|
||||||
}
|
|
||||||
Ok(count)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'i, W: FmtWrite> FmtWrite for Prettifier<'i, W> {
|
|
||||||
fn write_str(&mut self, s: &str) -> FmtResult {
|
|
||||||
for s in s.split_inclusive(['{', '}']) {
|
|
||||||
match s.as_bytes().last() {
|
|
||||||
Some(b'{') => self.level += 1,
|
|
||||||
Some(b'}') => self.level -= 1,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
for s in s.split_inclusive('\n') {
|
|
||||||
self.writer.write_str(s)?;
|
|
||||||
if let Some(b'\n') = s.as_bytes().last() {
|
|
||||||
self.fmt_write_indentation()?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'i, W: IoWrite> IoWrite for Prettifier<'i, W> {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
|
||||||
let mut size = 0;
|
|
||||||
for buf in buf.split_inclusive(|b| b"{}".contains(b)) {
|
|
||||||
match buf.last() {
|
|
||||||
Some(b'{') => self.level += 1,
|
|
||||||
Some(b'}') => self.level -= 1,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
for buf in buf.split_inclusive(|b| b'\n' == *b) {
|
|
||||||
size += self.writer.write(buf)?;
|
|
||||||
if let Some(b'\n') = buf.last() {
|
|
||||||
self.io_write_indentation()?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(size)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&mut self) -> std::io::Result<()> {
|
|
||||||
self.writer.flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,539 +0,0 @@
|
|||||||
//! # The Abstract Syntax Tree
|
|
||||||
//! Contains definitions of Conlang AST Nodes.
|
|
||||||
//!
|
|
||||||
//! # Notable nodes
|
|
||||||
//! - [Item] and [ItemKind]: Top-level constructs
|
|
||||||
//! - [Stmt] and [StmtKind]: Statements
|
|
||||||
//! - [Expr] and [ExprKind]: Expressions
|
|
||||||
//! - [Assign], [Binary], and [Unary] expressions
|
|
||||||
//! - [AssignKind], [BinaryKind], and [UnaryKind] operators
|
|
||||||
//! - [Ty] and [TyKind]: Type qualifiers
|
|
||||||
//! - [Path]: Path expressions
|
|
||||||
#![warn(clippy::all)]
|
|
||||||
#![feature(decl_macro)]
|
|
||||||
|
|
||||||
use cl_structures::span::*;
|
|
||||||
|
|
||||||
pub mod ast_impl;
|
|
||||||
pub mod format;
|
|
||||||
|
|
||||||
/// Whether a binding ([Static] or [Let]) or reference is mutable or not
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
|
||||||
pub enum Mutability {
|
|
||||||
#[default]
|
|
||||||
Not,
|
|
||||||
Mut,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether an [Item] is visible outside of the current [Module]
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
|
||||||
pub enum Visibility {
|
|
||||||
#[default]
|
|
||||||
Private,
|
|
||||||
Public,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A list of [Item]s
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
|
||||||
pub struct File {
|
|
||||||
pub items: Vec<Item>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Metadata decorators
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
|
||||||
pub struct Attrs {
|
|
||||||
pub meta: Vec<Meta>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A metadata decorator
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Meta {
|
|
||||||
pub name: Identifier,
|
|
||||||
pub kind: MetaKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Information attached to [Meta]data
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum MetaKind {
|
|
||||||
Plain,
|
|
||||||
Equals(Literal),
|
|
||||||
Func(Vec<Literal>),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Items
|
|
||||||
/// Anything that can appear at the top level of a [File]
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Item {
|
|
||||||
pub extents: Span,
|
|
||||||
pub attrs: Attrs,
|
|
||||||
pub vis: Visibility,
|
|
||||||
pub kind: ItemKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// What kind of [Item] is this?
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum ItemKind {
|
|
||||||
// TODO: Import declaration ("use") item
|
|
||||||
// TODO: Trait declaration ("trait") item?
|
|
||||||
/// A [module](Module)
|
|
||||||
Module(Module),
|
|
||||||
/// A [type alias](Alias)
|
|
||||||
Alias(Alias),
|
|
||||||
/// An [enumerated type](Enum), with a discriminant and optional data
|
|
||||||
Enum(Enum),
|
|
||||||
/// A [structure](Struct)
|
|
||||||
Struct(Struct),
|
|
||||||
/// A [constant](Const)
|
|
||||||
Const(Const),
|
|
||||||
/// A [static](Static) variable
|
|
||||||
Static(Static),
|
|
||||||
/// A [function definition](Function)
|
|
||||||
Function(Function),
|
|
||||||
/// An [implementation](Impl)
|
|
||||||
Impl(Impl),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An alias to another [Ty]
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Alias {
|
|
||||||
pub to: Identifier,
|
|
||||||
pub from: Option<Box<Ty>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A compile-time constant
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Const {
|
|
||||||
pub name: Identifier,
|
|
||||||
pub ty: Box<Ty>,
|
|
||||||
pub init: Box<Expr>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `static` variable
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Static {
|
|
||||||
pub mutable: Mutability,
|
|
||||||
pub name: Identifier,
|
|
||||||
pub ty: Box<Ty>,
|
|
||||||
pub init: Box<Expr>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An ordered collection of [Items](Item)
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Module {
|
|
||||||
pub name: Identifier,
|
|
||||||
pub kind: ModuleKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The contents of a [Module], if they're in the same file
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum ModuleKind {
|
|
||||||
Inline(File),
|
|
||||||
Outline,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Code, and the interface to that code
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Function {
|
|
||||||
pub name: Identifier,
|
|
||||||
pub args: Vec<Param>,
|
|
||||||
pub body: Option<Block>,
|
|
||||||
pub rety: Option<Box<Ty>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A single parameter for a [Function]
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Param {
|
|
||||||
pub mutability: Mutability,
|
|
||||||
pub name: Identifier,
|
|
||||||
pub ty: Box<Ty>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A user-defined product type
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Struct {
|
|
||||||
pub name: Identifier,
|
|
||||||
pub kind: StructKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Either a [Struct]'s [StructMember]s or tuple [Ty]pes, if present.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum StructKind {
|
|
||||||
Empty,
|
|
||||||
Tuple(Vec<Ty>),
|
|
||||||
Struct(Vec<StructMember>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The [Visibility], [Identifier], and [Ty]pe of a single [Struct] member
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct StructMember {
|
|
||||||
pub vis: Visibility,
|
|
||||||
pub name: Identifier,
|
|
||||||
pub ty: Ty,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A user-defined sum type
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Enum {
|
|
||||||
pub name: Identifier,
|
|
||||||
pub kind: EnumKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An [Enum]'s [Variant]s, if it has a variant block
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum EnumKind {
|
|
||||||
/// Represents an enum with no variants
|
|
||||||
NoVariants,
|
|
||||||
Variants(Vec<Variant>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A single [Enum] variant
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Variant {
|
|
||||||
pub name: Identifier,
|
|
||||||
pub kind: VariantKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether the [Variant] has a C-like constant value, a tuple, or [StructMember]s
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum VariantKind {
|
|
||||||
Plain,
|
|
||||||
CLike(u128),
|
|
||||||
Tuple(Vec<Ty>),
|
|
||||||
Struct(Vec<StructMember>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sub-[items](Item) (associated functions, etc.) for a [Ty]
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Impl {
|
|
||||||
pub target: Ty,
|
|
||||||
pub body: Vec<Item>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: `impl` Trait for <Target> { }
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum ImplKind {
|
|
||||||
Type(Box<Ty>),
|
|
||||||
Trait { impl_trait: Path, for_type: Box<Ty> },
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A type expression
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Ty {
|
|
||||||
pub extents: Span,
|
|
||||||
pub kind: TyKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Information about a [Ty]pe expression
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum TyKind {
|
|
||||||
Never,
|
|
||||||
Empty,
|
|
||||||
SelfTy,
|
|
||||||
Path(Path),
|
|
||||||
Tuple(TyTuple),
|
|
||||||
Ref(TyRef),
|
|
||||||
Fn(TyFn),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A tuple of [Ty]pes
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct TyTuple {
|
|
||||||
pub types: Vec<Ty>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [Ty]pe-reference expression as (number of `&`, [Path])
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct TyRef {
|
|
||||||
pub count: u16,
|
|
||||||
pub to: Path,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The args and return value for a function pointer [Ty]pe
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct TyFn {
|
|
||||||
pub args: TyTuple,
|
|
||||||
pub rety: Option<Box<Ty>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A path to an [Item] in the [Module] tree
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Path {
|
|
||||||
pub absolute: bool,
|
|
||||||
pub parts: Vec<PathPart>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A single component of a [Path]
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum PathPart {
|
|
||||||
SuperKw,
|
|
||||||
SelfKw,
|
|
||||||
Ident(Identifier),
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Capture token?
|
|
||||||
/// A name
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Identifier(pub String);
|
|
||||||
|
|
||||||
/// An abstract statement, and associated metadata
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Stmt {
|
|
||||||
pub extents: Span,
|
|
||||||
pub kind: StmtKind,
|
|
||||||
pub semi: Semi,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether or not a [Stmt] is followed by a semicolon
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum Semi {
|
|
||||||
Terminated,
|
|
||||||
Unterminated,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether the [Stmt] is a [Let], [Item], or [Expr] statement
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum StmtKind {
|
|
||||||
Empty,
|
|
||||||
Local(Let),
|
|
||||||
Item(Box<Item>),
|
|
||||||
Expr(Box<Expr>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A local variable declaration [Stmt]
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Let {
|
|
||||||
pub mutable: Mutability,
|
|
||||||
pub name: Identifier,
|
|
||||||
pub ty: Option<Box<Ty>>,
|
|
||||||
pub init: Option<Box<Expr>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An expression, the beating heart of the language
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Expr {
|
|
||||||
pub extents: Span,
|
|
||||||
pub kind: ExprKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Any of the different [Expr]essions
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum ExprKind {
|
|
||||||
/// An [Assign]ment expression: [`Expr`] ([`AssignKind`] [`Expr`])\+
|
|
||||||
Assign(Assign),
|
|
||||||
/// A [Binary] expression: [`Expr`] ([`BinaryKind`] [`Expr`])\+
|
|
||||||
Binary(Binary),
|
|
||||||
/// A [Unary] expression: [`UnaryKind`]\* [`Expr`]
|
|
||||||
Unary(Unary),
|
|
||||||
/// An Array [Index] expression: a[10, 20, 30]
|
|
||||||
Index(Index),
|
|
||||||
/// A [path expression](Path): `::`? [PathPart] (`::` [PathPart])*
|
|
||||||
Path(Path),
|
|
||||||
/// A [Literal]: 0x42, 1e123, 2.4, "Hello"
|
|
||||||
Literal(Literal),
|
|
||||||
/// An [Array] literal: `[` [`Expr`] (`,` [`Expr`])\* `]`
|
|
||||||
Array(Array),
|
|
||||||
/// An Array literal constructed with [repeat syntax](ArrayRep)
|
|
||||||
/// `[` [Expr] `;` [Literal] `]`
|
|
||||||
ArrayRep(ArrayRep),
|
|
||||||
/// An address-of expression: `&` `mut`? [`Expr`]
|
|
||||||
AddrOf(AddrOf),
|
|
||||||
/// A [Block] expression: `{` [`Stmt`]\* [`Expr`]? `}`
|
|
||||||
Block(Block),
|
|
||||||
/// An empty expression: `(` `)`
|
|
||||||
Empty,
|
|
||||||
/// A [Grouping](Group) expression `(` [`Expr`] `)`
|
|
||||||
Group(Group),
|
|
||||||
/// A [Tuple] expression: `(` [`Expr`] (`,` [`Expr`])+ `)`
|
|
||||||
Tuple(Tuple),
|
|
||||||
/// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]?
|
|
||||||
While(While),
|
|
||||||
/// An [If] expression: `if` [`Expr`] [`Block`] [`Else`]?
|
|
||||||
If(If),
|
|
||||||
/// A [For] expression: `for` Pattern `in` [`Expr`] [`Block`] [`Else`]?
|
|
||||||
For(For),
|
|
||||||
/// A [Break] expression: `break` [`Expr`]?
|
|
||||||
Break(Break),
|
|
||||||
/// A [Return] expression `return` [`Expr`]?
|
|
||||||
Return(Return),
|
|
||||||
/// A continue expression: `continue`
|
|
||||||
Continue(Continue),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An [Assign]ment expression: [`Expr`] ([`AssignKind`] [`Expr`])\+
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Assign {
|
|
||||||
pub kind: AssignKind,
|
|
||||||
pub parts: Box<(ExprKind, ExprKind)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub enum AssignKind {
|
|
||||||
/// Standard Assignment with no read-back
|
|
||||||
Plain,
|
|
||||||
And,
|
|
||||||
Or,
|
|
||||||
Xor,
|
|
||||||
Shl,
|
|
||||||
Shr,
|
|
||||||
Add,
|
|
||||||
Sub,
|
|
||||||
Mul,
|
|
||||||
Div,
|
|
||||||
Rem,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [Binary] expression: [`Expr`] ([`BinaryKind`] [`Expr`])\+
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Binary {
|
|
||||||
pub kind: BinaryKind,
|
|
||||||
pub parts: Box<(ExprKind, ExprKind)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [Binary] operator
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub enum BinaryKind {
|
|
||||||
Lt,
|
|
||||||
LtEq,
|
|
||||||
Equal,
|
|
||||||
NotEq,
|
|
||||||
GtEq,
|
|
||||||
Gt,
|
|
||||||
RangeExc,
|
|
||||||
RangeInc,
|
|
||||||
LogAnd,
|
|
||||||
LogOr,
|
|
||||||
LogXor,
|
|
||||||
BitAnd,
|
|
||||||
BitOr,
|
|
||||||
BitXor,
|
|
||||||
Shl,
|
|
||||||
Shr,
|
|
||||||
Add,
|
|
||||||
Sub,
|
|
||||||
Mul,
|
|
||||||
Div,
|
|
||||||
Rem,
|
|
||||||
Dot,
|
|
||||||
Call,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [Unary] expression: [`UnaryKind`]\* [`Expr`]
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Unary {
|
|
||||||
pub kind: UnaryKind,
|
|
||||||
pub tail: Box<ExprKind>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [Unary] operator
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub enum UnaryKind {
|
|
||||||
Deref,
|
|
||||||
Neg,
|
|
||||||
Not,
|
|
||||||
/// Unused
|
|
||||||
At,
|
|
||||||
/// Unused
|
|
||||||
Tilde,
|
|
||||||
}
|
|
||||||
/// A repeated [Index] expression: a[10, 20, 30][40, 50, 60]
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Index {
|
|
||||||
pub head: Box<ExprKind>,
|
|
||||||
pub indices: Vec<Expr>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [Literal]: 0x42, 1e123, 2.4, "Hello"
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum Literal {
|
|
||||||
Bool(bool),
|
|
||||||
Char(char),
|
|
||||||
Int(u128),
|
|
||||||
String(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An [Array] literal: `[` [`Expr`] (`,` [`Expr`])\* `]`
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Array {
|
|
||||||
pub values: Vec<Expr>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An Array literal constructed with [repeat syntax](ArrayRep)
|
|
||||||
/// `[` [Expr] `;` [Literal] `]`
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct ArrayRep {
|
|
||||||
pub value: Box<ExprKind>,
|
|
||||||
pub repeat: Box<ExprKind>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An address-of expression: `&` `mut`? [`Expr`]
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct AddrOf {
|
|
||||||
pub count: usize,
|
|
||||||
pub mutable: Mutability,
|
|
||||||
pub expr: Box<ExprKind>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [Block] expression: `{` [`Stmt`]\* [`Expr`]? `}`
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Block {
|
|
||||||
pub stmts: Vec<Stmt>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [Grouping](Group) expression `(` [`Expr`] `)`
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Group {
|
|
||||||
pub expr: Box<ExprKind>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [Tuple] expression: `(` [`Expr`] (`,` [`Expr`])+ `)`
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Tuple {
|
|
||||||
pub exprs: Vec<Expr>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]?
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct While {
|
|
||||||
pub cond: Box<Expr>,
|
|
||||||
pub pass: Box<Block>,
|
|
||||||
pub fail: Else,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An [If] expression: `if` [`Expr`] [`Block`] [`Else`]?
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct If {
|
|
||||||
pub cond: Box<Expr>,
|
|
||||||
pub pass: Box<Block>,
|
|
||||||
pub fail: Else,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [For] expression: `for` Pattern `in` [`Expr`] [`Block`] [`Else`]?
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct For {
|
|
||||||
pub bind: Identifier, // TODO: Patterns?
|
|
||||||
pub cond: Box<Expr>,
|
|
||||||
pub pass: Box<Block>,
|
|
||||||
pub fail: Else,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The (optional) `else` clause of a [While], [If], or [For] expression
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Else {
|
|
||||||
pub body: Option<Box<Expr>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [Break] expression: `break` [`Expr`]?
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Break {
|
|
||||||
pub body: Option<Box<Expr>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [Return] expression `return` [`Expr`]?
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Return {
|
|
||||||
pub body: Option<Box<Expr>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A continue expression: `continue`
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
|
||||||
pub struct Continue;
|
|
||||||
@@ -1,239 +0,0 @@
|
|||||||
//! Implementations of built-in functions
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
env::Environment,
|
|
||||||
error::{Error, IResult},
|
|
||||||
temp_type_impl::ConValue,
|
|
||||||
BuiltIn, Callable,
|
|
||||||
};
|
|
||||||
use std::io::{stdout, Write};
|
|
||||||
|
|
||||||
builtins! {
|
|
||||||
const MISC;
|
|
||||||
/// Unstable variadic print function
|
|
||||||
pub fn print<_, args> () -> IResult<ConValue> {
|
|
||||||
let mut out = stdout().lock();
|
|
||||||
for arg in args {
|
|
||||||
write!(out, "{arg}").ok();
|
|
||||||
}
|
|
||||||
writeln!(out).ok();
|
|
||||||
Ok(ConValue::Empty)
|
|
||||||
}
|
|
||||||
/// Prints the [Debug](std::fmt::Debug) version of the input values
|
|
||||||
pub fn dbg<_, args> () -> IResult<ConValue> {
|
|
||||||
let mut out = stdout().lock();
|
|
||||||
for arg in args {
|
|
||||||
writeln!(out, "{arg:?}").ok();
|
|
||||||
}
|
|
||||||
Ok(args.into())
|
|
||||||
}
|
|
||||||
/// Dumps info from the environment
|
|
||||||
pub fn dump<env, _>() -> IResult<ConValue> {
|
|
||||||
println!("{}", *env);
|
|
||||||
Ok(ConValue::Empty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
builtins! {
|
|
||||||
const BINARY;
|
|
||||||
/// Multiplication `a * b`
|
|
||||||
pub fn mul(lhs, rhs) -> IResult<ConValue> {
|
|
||||||
Ok(match (lhs, rhs) {
|
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a * b),
|
|
||||||
_ => Err(Error::TypeError)?
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/// Division `a / b`
|
|
||||||
pub fn div(lhs, rhs) -> IResult<ConValue> {
|
|
||||||
Ok(match (lhs, rhs){
|
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a / b),
|
|
||||||
_ => Err(Error::TypeError)?
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/// Remainder `a % b`
|
|
||||||
pub fn rem(lhs, rhs) -> IResult<ConValue> {
|
|
||||||
Ok(match (lhs, rhs) {
|
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a % b),
|
|
||||||
_ => Err(Error::TypeError)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Addition `a + b`
|
|
||||||
pub fn add(lhs, rhs) -> IResult<ConValue> {
|
|
||||||
Ok(match (lhs, rhs) {
|
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b),
|
|
||||||
(ConValue::String(a), ConValue::String(b)) => ConValue::String(a.to_string() + b),
|
|
||||||
_ => Err(Error::TypeError)?
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/// Subtraction `a - b`
|
|
||||||
pub fn sub(lhs, rhs) -> IResult<ConValue> {
|
|
||||||
Ok(match (lhs, rhs) {
|
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a - b),
|
|
||||||
_ => Err(Error::TypeError)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Shift Left `a << b`
|
|
||||||
pub fn shl(lhs, rhs) -> IResult<ConValue> {
|
|
||||||
Ok(match (lhs, rhs) {
|
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a << b),
|
|
||||||
_ => Err(Error::TypeError)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/// Shift Right `a >> b`
|
|
||||||
pub fn shr(lhs, rhs) -> IResult<ConValue> {
|
|
||||||
Ok(match (lhs, rhs) {
|
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a >> b),
|
|
||||||
_ => Err(Error::TypeError)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Bitwise And `a & b`
|
|
||||||
pub fn and(lhs, rhs) -> IResult<ConValue> {
|
|
||||||
Ok(match (lhs, rhs) {
|
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
|
|
||||||
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
|
|
||||||
_ => Err(Error::TypeError)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/// Bitwise Or `a | b`
|
|
||||||
pub fn or(lhs, rhs) -> IResult<ConValue> {
|
|
||||||
Ok(match (lhs, rhs) {
|
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
|
|
||||||
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
|
|
||||||
_ => Err(Error::TypeError)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/// Bitwise Exclusive Or `a ^ b`
|
|
||||||
pub fn xor(lhs, rhs) -> IResult<ConValue> {
|
|
||||||
Ok(match (lhs, rhs) {
|
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
|
|
||||||
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
|
|
||||||
_ => Err(Error::TypeError)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tests whether `a < b`
|
|
||||||
pub fn lt(lhs, rhs) -> IResult<ConValue> {
|
|
||||||
cmp!(lhs, rhs, false, <)
|
|
||||||
}
|
|
||||||
/// Tests whether `a <= b`
|
|
||||||
pub fn lt_eq(lhs, rhs) -> IResult<ConValue> {
|
|
||||||
cmp!(lhs, rhs, true, <=)
|
|
||||||
}
|
|
||||||
/// Tests whether `a == b`
|
|
||||||
pub fn eq(lhs, rhs) -> IResult<ConValue> {
|
|
||||||
cmp!(lhs, rhs, true, ==)
|
|
||||||
}
|
|
||||||
/// Tests whether `a != b`
|
|
||||||
pub fn neq(lhs, rhs) -> IResult<ConValue> {
|
|
||||||
cmp!(lhs, rhs, false, !=)
|
|
||||||
}
|
|
||||||
/// Tests whether `a <= b`
|
|
||||||
pub fn gt_eq(lhs, rhs) -> IResult<ConValue> {
|
|
||||||
cmp!(lhs, rhs, true, >=)
|
|
||||||
}
|
|
||||||
/// Tests whether `a < b`
|
|
||||||
pub fn gt(lhs, rhs) -> IResult<ConValue> {
|
|
||||||
cmp!(lhs, rhs, false, >)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
builtins! {
|
|
||||||
const RANGE;
|
|
||||||
/// Exclusive Range `a..b`
|
|
||||||
pub fn range_exc(lhs, rhs) -> IResult<ConValue> {
|
|
||||||
let (&ConValue::Int(lhs), &ConValue::Int(rhs)) = (lhs, rhs) else {
|
|
||||||
Err(Error::TypeError)?
|
|
||||||
};
|
|
||||||
Ok(ConValue::RangeExc(lhs, rhs.saturating_sub(1)))
|
|
||||||
}
|
|
||||||
/// Inclusive Range `a..=b`
|
|
||||||
pub fn range_inc(lhs, rhs) -> IResult<ConValue> {
|
|
||||||
let (&ConValue::Int(lhs), &ConValue::Int(rhs)) = (lhs, rhs) else {
|
|
||||||
Err(Error::TypeError)?
|
|
||||||
};
|
|
||||||
Ok(ConValue::RangeInc(lhs, rhs))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
builtins! {
|
|
||||||
const UNARY;
|
|
||||||
/// Negates the ConValue
|
|
||||||
pub fn neg(tail) -> IResult<ConValue> {
|
|
||||||
Ok(match tail {
|
|
||||||
ConValue::Empty => ConValue::Empty,
|
|
||||||
ConValue::Int(v) => ConValue::Int(-v),
|
|
||||||
_ => Err(Error::TypeError)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/// Inverts the ConValue
|
|
||||||
pub fn not(tail) -> IResult<ConValue> {
|
|
||||||
Ok(match tail {
|
|
||||||
ConValue::Empty => ConValue::Empty,
|
|
||||||
ConValue::Int(v) => ConValue::Int(!v),
|
|
||||||
ConValue::Bool(v) => ConValue::Bool(!v),
|
|
||||||
_ => Err(Error::TypeError)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Turns an argument slice into an array with the (inferred) correct number of elements
|
|
||||||
pub fn to_args<const N: usize>(args: &[ConValue]) -> IResult<&[ConValue; N]> {
|
|
||||||
args.try_into()
|
|
||||||
.map_err(|_| Error::ArgNumber { want: N, got: args.len() })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Turns function definitions into ZSTs which implement [Callable] and [BuiltIn]
|
|
||||||
macro builtins (
|
|
||||||
$(prefix = $prefix:literal)?
|
|
||||||
const $defaults:ident $( = [$($additional_builtins:expr),*$(,)?])?;
|
|
||||||
$(
|
|
||||||
$(#[$meta:meta])*$vis:vis fn $name:ident$(<$env:tt, $args:tt>)? ( $($($arg:tt),+$(,)?)? ) $(-> $rety:ty)?
|
|
||||||
$body:block
|
|
||||||
)*
|
|
||||||
) {
|
|
||||||
/// Builtins to load when a new interpreter is created
|
|
||||||
pub const $defaults: &[&dyn BuiltIn] = &[$(&$name,)* $($additional_builtins)*];
|
|
||||||
$(
|
|
||||||
$(#[$meta])* #[allow(non_camel_case_types)] #[derive(Clone, Debug)]
|
|
||||||
/// ```rust,ignore
|
|
||||||
#[doc = stringify!(builtin! fn $name($($($arg),*)?) $(-> $rety)? $body)]
|
|
||||||
/// ```
|
|
||||||
$vis struct $name;
|
|
||||||
impl BuiltIn for $name {
|
|
||||||
fn description(&self) -> &str { concat!("builtin ", stringify!($name), stringify!(($($($arg),*)?) )) }
|
|
||||||
}
|
|
||||||
impl Callable for $name {
|
|
||||||
#[allow(unused)]
|
|
||||||
fn call(&self, env: &mut Environment, args: &[ConValue]) $(-> $rety)? {
|
|
||||||
// println!("{}", stringify!($name), );
|
|
||||||
$(let $env = env;
|
|
||||||
let $args = args;)?
|
|
||||||
$(let [$($arg),*] = to_args(args)?;)?
|
|
||||||
$body
|
|
||||||
}
|
|
||||||
fn name(&self) -> &str { stringify!($name) }
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Templates comparison functions for [ConValue]
|
|
||||||
macro cmp ($a:expr, $b:expr, $empty:literal, $op:tt) {
|
|
||||||
match ($a, $b) {
|
|
||||||
(ConValue::Empty, ConValue::Empty) => Ok(ConValue::Bool($empty)),
|
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => Ok(ConValue::Bool(a $op b)),
|
|
||||||
(ConValue::Bool(a), ConValue::Bool(b)) => Ok(ConValue::Bool(a $op b)),
|
|
||||||
(ConValue::Char(a), ConValue::Char(b)) => Ok(ConValue::Bool(a $op b)),
|
|
||||||
(ConValue::String(a), ConValue::String(b)) => Ok(ConValue::Bool(a $op b)),
|
|
||||||
_ => Err(Error::TypeError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,447 +0,0 @@
|
|||||||
//! A work-in-progress tree walk interpreter for Conlang
|
|
||||||
//!
|
|
||||||
//! Currently, major parts of the interpreter are not yet implemented, and major parts will never be
|
|
||||||
//! implemented in its current form. Namely, since no [ConValue] has a stable location, it's
|
|
||||||
//! meaningless to get a pointer to one, and would be undefined behavior to dereference a pointer to
|
|
||||||
//! one in any situation.
|
|
||||||
|
|
||||||
use std::borrow::Borrow;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use cl_ast::*;
|
|
||||||
/// A work-in-progress tree walk interpreter for Conlang
|
|
||||||
pub trait Interpret {
|
|
||||||
/// Interprets this thing in the given [`Environment`].
|
|
||||||
///
|
|
||||||
/// Everything returns a value!™
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Interpret for File {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
for item in &self.items {
|
|
||||||
item.interpret(env)?;
|
|
||||||
}
|
|
||||||
Ok(ConValue::Empty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Item {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
match &self.kind {
|
|
||||||
ItemKind::Alias(item) => item.interpret(env),
|
|
||||||
ItemKind::Const(item) => item.interpret(env),
|
|
||||||
ItemKind::Static(item) => item.interpret(env),
|
|
||||||
ItemKind::Module(item) => item.interpret(env),
|
|
||||||
ItemKind::Function(item) => item.interpret(env),
|
|
||||||
ItemKind::Struct(item) => item.interpret(env),
|
|
||||||
ItemKind::Enum(item) => item.interpret(env),
|
|
||||||
ItemKind::Impl(item) => item.interpret(env),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Alias {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
todo!("Interpret type alias in {env}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Const {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
todo!("interpret const in {env}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Static {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
todo!("interpret static in {env}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Module {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
// TODO: Enter this module's namespace
|
|
||||||
match &self.kind {
|
|
||||||
ModuleKind::Inline(file) => file.interpret(env),
|
|
||||||
ModuleKind::Outline => todo!("Load and parse external files"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Function {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
// register the function in the current environment
|
|
||||||
env.insert_fn(self);
|
|
||||||
Ok(ConValue::Empty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Struct {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
todo!("Interpret structs in {env}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Enum {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
todo!("Interpret enums in {env}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Impl {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
todo!("Enter a struct's namespace and insert function definitions into it in {env}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Stmt {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
let Self { extents: _, kind, semi } = self;
|
|
||||||
let out = match kind {
|
|
||||||
StmtKind::Empty => ConValue::Empty,
|
|
||||||
StmtKind::Local(stmt) => stmt.interpret(env)?,
|
|
||||||
StmtKind::Item(stmt) => stmt.interpret(env)?,
|
|
||||||
StmtKind::Expr(stmt) => stmt.interpret(env)?,
|
|
||||||
};
|
|
||||||
Ok(match semi {
|
|
||||||
Semi::Terminated => ConValue::Empty,
|
|
||||||
Semi::Unterminated => out,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Let {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
let Let { mutable: _, name: Identifier(name), ty: _, init } = self;
|
|
||||||
let init = init.as_ref().map(|i| i.interpret(env)).transpose()?;
|
|
||||||
env.insert(name, init);
|
|
||||||
Ok(ConValue::Empty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Expr {
|
|
||||||
#[inline]
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
let Self { extents: _, kind } = self;
|
|
||||||
kind.interpret(env)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for ExprKind {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
match self {
|
|
||||||
ExprKind::Assign(v) => v.interpret(env),
|
|
||||||
ExprKind::Binary(v) => v.interpret(env),
|
|
||||||
ExprKind::Unary(v) => v.interpret(env),
|
|
||||||
ExprKind::Index(v) => v.interpret(env),
|
|
||||||
ExprKind::Path(v) => v.interpret(env),
|
|
||||||
ExprKind::Literal(v) => v.interpret(env),
|
|
||||||
ExprKind::Array(v) => v.interpret(env),
|
|
||||||
ExprKind::ArrayRep(v) => v.interpret(env),
|
|
||||||
ExprKind::AddrOf(v) => v.interpret(env),
|
|
||||||
ExprKind::Block(v) => v.interpret(env),
|
|
||||||
ExprKind::Empty => Ok(ConValue::Empty),
|
|
||||||
ExprKind::Group(v) => v.interpret(env),
|
|
||||||
ExprKind::Tuple(v) => v.interpret(env),
|
|
||||||
ExprKind::While(v) => v.interpret(env),
|
|
||||||
ExprKind::If(v) => v.interpret(env),
|
|
||||||
ExprKind::For(v) => v.interpret(env),
|
|
||||||
ExprKind::Break(v) => v.interpret(env),
|
|
||||||
ExprKind::Return(v) => v.interpret(env),
|
|
||||||
ExprKind::Continue(v) => v.interpret(env),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Assign {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
let Assign { kind: op, parts } = self;
|
|
||||||
let (head, tail) = parts.borrow();
|
|
||||||
// Resolve the head pattern
|
|
||||||
let head = match &head {
|
|
||||||
ExprKind::Path(Path { parts, .. }) if parts.len() == 1 => {
|
|
||||||
match parts.last().expect("parts should not be empty") {
|
|
||||||
PathPart::SuperKw => Err(Error::NotAssignable)?,
|
|
||||||
PathPart::SelfKw => todo!("Assignment to `self`"),
|
|
||||||
PathPart::Ident(Identifier(s)) => s,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ExprKind::Index(_) => todo!("Assignment to an index operation"),
|
|
||||||
ExprKind::Path(_) => todo!("Path expression resolution (IMPORTANT)"),
|
|
||||||
ExprKind::Empty | ExprKind::Group(_) | ExprKind::Tuple(_) => {
|
|
||||||
todo!("Pattern Destructuring?")
|
|
||||||
}
|
|
||||||
_ => Err(Error::NotAssignable)?,
|
|
||||||
};
|
|
||||||
// Get the initializer and the tail
|
|
||||||
let init = tail.interpret(env)?;
|
|
||||||
let target = env.get_mut(head)?;
|
|
||||||
|
|
||||||
if let AssignKind::Plain = op {
|
|
||||||
use std::mem::discriminant as variant;
|
|
||||||
// runtime typecheck
|
|
||||||
match target {
|
|
||||||
Some(value) if variant(value) == variant(&init) => {
|
|
||||||
*value = init;
|
|
||||||
}
|
|
||||||
value @ None => *value = Some(init),
|
|
||||||
_ => Err(Error::TypeError)?,
|
|
||||||
}
|
|
||||||
return Ok(ConValue::Empty);
|
|
||||||
}
|
|
||||||
let Some(target) = target else {
|
|
||||||
return Err(Error::NotInitialized(head.into()));
|
|
||||||
};
|
|
||||||
|
|
||||||
match op {
|
|
||||||
AssignKind::Add => target.add_assign(init)?,
|
|
||||||
AssignKind::Sub => target.sub_assign(init)?,
|
|
||||||
AssignKind::Mul => target.mul_assign(init)?,
|
|
||||||
AssignKind::Div => target.div_assign(init)?,
|
|
||||||
AssignKind::Rem => target.rem_assign(init)?,
|
|
||||||
AssignKind::And => target.bitand_assign(init)?,
|
|
||||||
AssignKind::Or => target.bitor_assign(init)?,
|
|
||||||
AssignKind::Xor => target.bitxor_assign(init)?,
|
|
||||||
AssignKind::Shl => target.shl_assign(init)?,
|
|
||||||
AssignKind::Shr => target.shr_assign(init)?,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
Ok(ConValue::Empty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Binary {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
let Binary { kind, parts } = self;
|
|
||||||
let (head, tail) = parts.borrow();
|
|
||||||
|
|
||||||
let head = head.interpret(env)?;
|
|
||||||
|
|
||||||
// Short-circuiting ops
|
|
||||||
match kind {
|
|
||||||
BinaryKind::LogAnd => {
|
|
||||||
return if head.truthy()? {
|
|
||||||
tail.interpret(env)
|
|
||||||
} else {
|
|
||||||
Ok(head)
|
|
||||||
}; // Short circuiting
|
|
||||||
}
|
|
||||||
BinaryKind::LogOr => {
|
|
||||||
return if !head.truthy()? {
|
|
||||||
tail.interpret(env)
|
|
||||||
} else {
|
|
||||||
Ok(head)
|
|
||||||
}; // Short circuiting
|
|
||||||
}
|
|
||||||
BinaryKind::LogXor => {
|
|
||||||
return Ok(ConValue::Bool(
|
|
||||||
head.truthy()? ^ tail.interpret(env)?.truthy()?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
let tail = tail.interpret(env)?;
|
|
||||||
match kind {
|
|
||||||
BinaryKind::Mul => env.call("mul", &[head, tail]),
|
|
||||||
BinaryKind::Div => env.call("div", &[head, tail]),
|
|
||||||
BinaryKind::Rem => env.call("rem", &[head, tail]),
|
|
||||||
BinaryKind::Add => env.call("add", &[head, tail]),
|
|
||||||
BinaryKind::Sub => env.call("sub", &[head, tail]),
|
|
||||||
BinaryKind::Shl => env.call("shl", &[head, tail]),
|
|
||||||
BinaryKind::Shr => env.call("shr", &[head, tail]),
|
|
||||||
BinaryKind::BitAnd => env.call("and", &[head, tail]),
|
|
||||||
BinaryKind::BitOr => env.call("or", &[head, tail]),
|
|
||||||
BinaryKind::BitXor => env.call("xor", &[head, tail]),
|
|
||||||
BinaryKind::RangeExc => env.call("range_exc", &[head, tail]),
|
|
||||||
BinaryKind::RangeInc => env.call("range_inc", &[head, tail]),
|
|
||||||
BinaryKind::Lt => env.call("lt", &[head, tail]),
|
|
||||||
BinaryKind::LtEq => env.call("lt_eq", &[head, tail]),
|
|
||||||
BinaryKind::Equal => env.call("eq", &[head, tail]),
|
|
||||||
BinaryKind::NotEq => env.call("neq", &[head, tail]),
|
|
||||||
BinaryKind::GtEq => env.call("gt_eq", &[head, tail]),
|
|
||||||
BinaryKind::Gt => env.call("gt", &[head, tail]),
|
|
||||||
BinaryKind::Dot => todo!("search within a type's namespace!"),
|
|
||||||
BinaryKind::Call => match tail {
|
|
||||||
ConValue::Empty => head.call(env, &[]),
|
|
||||||
ConValue::Tuple(args) => head.call(env, &args),
|
|
||||||
_ => Err(Error::TypeError),
|
|
||||||
},
|
|
||||||
_ => Ok(head),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Interpret for Unary {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
let Unary { kind, tail } = self;
|
|
||||||
let operand = tail.interpret(env)?;
|
|
||||||
match kind {
|
|
||||||
UnaryKind::Deref => env.call("deref", &[operand]),
|
|
||||||
UnaryKind::Neg => env.call("neg", &[operand]),
|
|
||||||
UnaryKind::Not => env.call("not", &[operand]),
|
|
||||||
UnaryKind::At => {
|
|
||||||
println!("{operand}");
|
|
||||||
Ok(operand)
|
|
||||||
}
|
|
||||||
UnaryKind::Tilde => unimplemented!("Tilde operator"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Index {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
let Self { head, indices } = self;
|
|
||||||
let mut head = head.interpret(env)?;
|
|
||||||
for index in indices {
|
|
||||||
head = head.index(&index.interpret(env)?)?;
|
|
||||||
}
|
|
||||||
Ok(head)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Path {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
let Self { absolute: _, parts } = self;
|
|
||||||
|
|
||||||
if parts.len() == 1 {
|
|
||||||
match parts.last().expect("parts should not be empty") {
|
|
||||||
PathPart::SuperKw | PathPart::SelfKw => todo!("Path navigation"),
|
|
||||||
PathPart::Ident(Identifier(s)) => env.get(s).cloned(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
todo!("Path navigation!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Literal {
|
|
||||||
fn interpret(&self, _env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
Ok(match self {
|
|
||||||
Literal::String(value) => ConValue::from(value.as_str()),
|
|
||||||
Literal::Char(value) => ConValue::Char(*value),
|
|
||||||
Literal::Bool(value) => ConValue::Bool(*value),
|
|
||||||
// Literal::Float(value) => todo!("Float values in interpreter: {value:?}"),
|
|
||||||
Literal::Int(value) => ConValue::Int(*value as _),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Array {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
let Self { values } = self;
|
|
||||||
let mut out = vec![];
|
|
||||||
for expr in values {
|
|
||||||
out.push(expr.interpret(env)?)
|
|
||||||
}
|
|
||||||
Ok(ConValue::Array(out))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for ArrayRep {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
let Self { value, repeat } = self;
|
|
||||||
let repeat = match repeat.interpret(env)? {
|
|
||||||
ConValue::Int(v) => v,
|
|
||||||
_ => Err(Error::TypeError)?,
|
|
||||||
};
|
|
||||||
let value = value.interpret(env)?;
|
|
||||||
Ok(ConValue::Array(vec![value; repeat as usize]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for AddrOf {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
let Self { count: _, mutable: _, expr } = self;
|
|
||||||
// this is stupid
|
|
||||||
todo!("Create reference\nfrom expr: {expr}\nin env:\n{env}\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Block {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
let Self { stmts } = self;
|
|
||||||
let mut env = env.frame("block");
|
|
||||||
let mut out = ConValue::Empty;
|
|
||||||
for stmt in stmts {
|
|
||||||
out = stmt.interpret(&mut env)?;
|
|
||||||
}
|
|
||||||
Ok(out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Group {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
let Self { expr } = self;
|
|
||||||
expr.interpret(env)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Tuple {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
let Self { exprs } = self;
|
|
||||||
Ok(ConValue::Tuple(exprs.iter().try_fold(
|
|
||||||
vec![],
|
|
||||||
|mut out, element| {
|
|
||||||
out.push(element.interpret(env)?);
|
|
||||||
Ok(out)
|
|
||||||
},
|
|
||||||
)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for While {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
let Self { cond, pass, fail } = self;
|
|
||||||
while cond.interpret(env)?.truthy()? {
|
|
||||||
match pass.interpret(env) {
|
|
||||||
Err(Error::Break(value)) => return Ok(value),
|
|
||||||
Err(Error::Continue) => continue,
|
|
||||||
e => e?,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
fail.interpret(env)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for If {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
let Self { cond, pass, fail } = self;
|
|
||||||
if cond.interpret(env)?.truthy()? {
|
|
||||||
pass.interpret(env)
|
|
||||||
} else {
|
|
||||||
fail.interpret(env)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for For {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
let Self { bind: Identifier(name), cond, pass, fail } = self;
|
|
||||||
// TODO: A better iterator model
|
|
||||||
let bounds = match cond.interpret(env)? {
|
|
||||||
ConValue::RangeExc(a, b) => a..=b,
|
|
||||||
ConValue::RangeInc(a, b) => a..=b,
|
|
||||||
_ => Err(Error::TypeError)?,
|
|
||||||
};
|
|
||||||
{
|
|
||||||
let mut env = env.frame("loop variable");
|
|
||||||
for loop_var in bounds {
|
|
||||||
env.insert(name, Some(loop_var.into()));
|
|
||||||
match pass.interpret(&mut env) {
|
|
||||||
Err(Error::Break(value)) => return Ok(value),
|
|
||||||
Err(Error::Continue) => continue,
|
|
||||||
result => result?,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fail.interpret(env)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Else {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
let Self { body } = self;
|
|
||||||
match body {
|
|
||||||
Some(body) => body.interpret(env),
|
|
||||||
None => Ok(ConValue::Empty),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Continue {
|
|
||||||
fn interpret(&self, _env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
Err(Error::Continue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Return {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
let Self { body } = self;
|
|
||||||
Err(Error::Return(
|
|
||||||
body.as_ref()
|
|
||||||
.map(|body| body.interpret(env))
|
|
||||||
.unwrap_or(Ok(ConValue::Empty))?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Interpret for Break {
|
|
||||||
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
let Self { body } = self;
|
|
||||||
Err(Error::Break(
|
|
||||||
body.as_ref()
|
|
||||||
.map(|body| body.interpret(env))
|
|
||||||
.unwrap_or(Ok(ConValue::Empty))?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,614 +0,0 @@
|
|||||||
//! Walks a Conlang AST, interpreting it as a program.
|
|
||||||
#![warn(clippy::all)]
|
|
||||||
#![feature(decl_macro)]
|
|
||||||
|
|
||||||
use env::Environment;
|
|
||||||
use error::{Error, IResult};
|
|
||||||
use interpret::Interpret;
|
|
||||||
use temp_type_impl::ConValue;
|
|
||||||
|
|
||||||
/// Callable types can be called from within a Conlang program
|
|
||||||
pub trait Callable: std::fmt::Debug {
|
|
||||||
/// Calls this [Callable] in the provided [Environment], with [ConValue] args \
|
|
||||||
/// The Callable is responsible for checking the argument count and validating types
|
|
||||||
fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue>;
|
|
||||||
/// Returns the common name of this identifier.
|
|
||||||
fn name(&self) -> &str;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [BuiltIn]s are [Callable]s with bespoke definitions
|
|
||||||
pub trait BuiltIn: std::fmt::Debug + Callable {
|
|
||||||
fn description(&self) -> &str;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod temp_type_impl {
|
|
||||||
//! Temporary implementations of Conlang values
|
|
||||||
//!
|
|
||||||
//! The most permanent fix is a temporary one.
|
|
||||||
use super::{
|
|
||||||
error::{Error, IResult},
|
|
||||||
function::Function,
|
|
||||||
BuiltIn, Callable, Environment,
|
|
||||||
};
|
|
||||||
use std::ops::*;
|
|
||||||
|
|
||||||
type Integer = isize;
|
|
||||||
|
|
||||||
/// A Conlang value
|
|
||||||
///
|
|
||||||
/// This is a hack to work around the fact that Conlang doesn't
|
|
||||||
/// have a functioning type system yet :(
|
|
||||||
#[derive(Clone, Debug, Default)]
|
|
||||||
pub enum ConValue {
|
|
||||||
/// The empty/unit `()` type
|
|
||||||
#[default]
|
|
||||||
Empty,
|
|
||||||
/// An integer
|
|
||||||
Int(Integer),
|
|
||||||
/// A boolean
|
|
||||||
Bool(bool),
|
|
||||||
/// A unicode character
|
|
||||||
Char(char),
|
|
||||||
/// A string
|
|
||||||
String(String),
|
|
||||||
/// An Array
|
|
||||||
Array(Vec<ConValue>),
|
|
||||||
/// A tuple
|
|
||||||
Tuple(Vec<ConValue>),
|
|
||||||
/// An exclusive range
|
|
||||||
RangeExc(Integer, Integer),
|
|
||||||
/// An inclusive range
|
|
||||||
RangeInc(Integer, Integer),
|
|
||||||
/// A callable thing
|
|
||||||
Function(Function),
|
|
||||||
/// A built-in function
|
|
||||||
BuiltIn(&'static dyn BuiltIn),
|
|
||||||
}
|
|
||||||
impl ConValue {
|
|
||||||
/// Gets whether the current value is true or false
|
|
||||||
pub fn truthy(&self) -> IResult<bool> {
|
|
||||||
match self {
|
|
||||||
ConValue::Bool(v) => Ok(*v),
|
|
||||||
_ => Err(Error::TypeError)?,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn range_exc(self, other: Self) -> IResult<Self> {
|
|
||||||
let (Self::Int(a), Self::Int(b)) = (self, other) else {
|
|
||||||
Err(Error::TypeError)?
|
|
||||||
};
|
|
||||||
Ok(Self::RangeExc(a, b.saturating_sub(1)))
|
|
||||||
}
|
|
||||||
pub fn range_inc(self, other: Self) -> IResult<Self> {
|
|
||||||
let (Self::Int(a), Self::Int(b)) = (self, other) else {
|
|
||||||
Err(Error::TypeError)?
|
|
||||||
};
|
|
||||||
Ok(Self::RangeInc(a, b))
|
|
||||||
}
|
|
||||||
pub fn index(&self, index: &Self) -> IResult<ConValue> {
|
|
||||||
let Self::Int(index) = index else {
|
|
||||||
Err(Error::TypeError)?
|
|
||||||
};
|
|
||||||
let Self::Array(arr) = self else {
|
|
||||||
Err(Error::TypeError)?
|
|
||||||
};
|
|
||||||
arr.get(*index as usize)
|
|
||||||
.cloned()
|
|
||||||
.ok_or(Error::OobIndex(*index as usize, arr.len()))
|
|
||||||
}
|
|
||||||
cmp! {
|
|
||||||
lt: false, <;
|
|
||||||
lt_eq: true, <=;
|
|
||||||
eq: true, ==;
|
|
||||||
neq: false, !=;
|
|
||||||
gt_eq: true, >=;
|
|
||||||
gt: false, >;
|
|
||||||
}
|
|
||||||
assign! {
|
|
||||||
add_assign: +;
|
|
||||||
bitand_assign: &;
|
|
||||||
bitor_assign: |;
|
|
||||||
bitxor_assign: ^;
|
|
||||||
div_assign: /;
|
|
||||||
mul_assign: *;
|
|
||||||
rem_assign: %;
|
|
||||||
shl_assign: <<;
|
|
||||||
shr_assign: >>;
|
|
||||||
sub_assign: -;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Callable for ConValue {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
ConValue::Function(func) => func.name(),
|
|
||||||
ConValue::BuiltIn(func) => func.name(),
|
|
||||||
_ => "",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
|
|
||||||
match self {
|
|
||||||
Self::Function(func) => func.call(interpreter, args),
|
|
||||||
Self::BuiltIn(func) => func.call(interpreter, args),
|
|
||||||
_ => Err(Error::NotCallable(self.clone())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Templates comparison functions for [ConValue]
|
|
||||||
macro cmp ($($fn:ident: $empty:literal, $op:tt);*$(;)?) {$(
|
|
||||||
/// TODO: Remove when functions are implemented:
|
|
||||||
/// Desugar into function calls
|
|
||||||
pub fn $fn(&self, other: &Self) -> IResult<Self> {
|
|
||||||
match (self, other) {
|
|
||||||
(Self::Empty, Self::Empty) => Ok(Self::Bool($empty)),
|
|
||||||
(Self::Int(a), Self::Int(b)) => Ok(Self::Bool(a $op b)),
|
|
||||||
(Self::Bool(a), Self::Bool(b)) => Ok(Self::Bool(a $op b)),
|
|
||||||
(Self::Char(a), Self::Char(b)) => Ok(Self::Bool(a $op b)),
|
|
||||||
(Self::String(a), Self::String(b)) => Ok(Self::Bool(a $op b)),
|
|
||||||
_ => Err(Error::TypeError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*}
|
|
||||||
macro assign($( $fn: ident: $op: tt );*$(;)?) {$(
|
|
||||||
pub fn $fn(&mut self, other: Self) -> IResult<()> {
|
|
||||||
*self = (std::mem::take(self) $op other)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
)*}
|
|
||||||
/// Implements [From] for an enum with 1-tuple variants
|
|
||||||
macro from ($($T:ty => $v:expr),*$(,)?) {
|
|
||||||
$(impl From<$T> for ConValue {
|
|
||||||
fn from(value: $T) -> Self { $v(value.into()) }
|
|
||||||
})*
|
|
||||||
}
|
|
||||||
from! {
|
|
||||||
Integer => ConValue::Int,
|
|
||||||
bool => ConValue::Bool,
|
|
||||||
char => ConValue::Char,
|
|
||||||
&str => ConValue::String,
|
|
||||||
String => ConValue::String,
|
|
||||||
Function => ConValue::Function,
|
|
||||||
Vec<ConValue> => ConValue::Tuple,
|
|
||||||
&'static dyn BuiltIn => ConValue::BuiltIn,
|
|
||||||
}
|
|
||||||
impl From<()> for ConValue {
|
|
||||||
fn from(_: ()) -> Self {
|
|
||||||
Self::Empty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<&[ConValue]> for ConValue {
|
|
||||||
fn from(value: &[ConValue]) -> Self {
|
|
||||||
match value.len() {
|
|
||||||
0 => Self::Empty,
|
|
||||||
1 => value[0].clone(),
|
|
||||||
_ => Self::Tuple(value.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implements binary [std::ops] traits for [ConValue]
|
|
||||||
///
|
|
||||||
/// TODO: Desugar operators into function calls
|
|
||||||
macro ops($($trait:ty: $fn:ident = [$($match:tt)*])*) {
|
|
||||||
$(impl $trait for ConValue {
|
|
||||||
type Output = IResult<Self>;
|
|
||||||
/// TODO: Desugar operators into function calls
|
|
||||||
fn $fn(self, rhs: Self) -> Self::Output {Ok(match (self, rhs) {$($match)*})}
|
|
||||||
})*
|
|
||||||
}
|
|
||||||
ops! {
|
|
||||||
Add: add = [
|
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b),
|
|
||||||
(ConValue::String(a), ConValue::String(b)) => ConValue::String(a + &b),
|
|
||||||
_ => Err(Error::TypeError)?
|
|
||||||
]
|
|
||||||
BitAnd: bitand = [
|
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
|
|
||||||
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
|
|
||||||
_ => Err(Error::TypeError)?
|
|
||||||
]
|
|
||||||
BitOr: bitor = [
|
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
|
|
||||||
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
|
|
||||||
_ => Err(Error::TypeError)?
|
|
||||||
]
|
|
||||||
BitXor: bitxor = [
|
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
|
|
||||||
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
|
|
||||||
_ => Err(Error::TypeError)?
|
|
||||||
]
|
|
||||||
Div: div = [
|
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a / b),
|
|
||||||
_ => Err(Error::TypeError)?
|
|
||||||
]
|
|
||||||
Mul: mul = [
|
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a * b),
|
|
||||||
_ => Err(Error::TypeError)?
|
|
||||||
]
|
|
||||||
Rem: rem = [
|
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a % b),
|
|
||||||
_ => Err(Error::TypeError)?
|
|
||||||
]
|
|
||||||
Shl: shl = [
|
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a << b),
|
|
||||||
_ => Err(Error::TypeError)?
|
|
||||||
]
|
|
||||||
Shr: shr = [
|
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a >> b),
|
|
||||||
_ => Err(Error::TypeError)?
|
|
||||||
]
|
|
||||||
Sub: sub = [
|
|
||||||
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
||||||
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a - b),
|
|
||||||
_ => Err(Error::TypeError)?
|
|
||||||
]
|
|
||||||
}
|
|
||||||
impl std::fmt::Display for ConValue {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
ConValue::Empty => "Empty".fmt(f),
|
|
||||||
ConValue::Int(v) => v.fmt(f),
|
|
||||||
ConValue::Bool(v) => v.fmt(f),
|
|
||||||
ConValue::Char(v) => v.fmt(f),
|
|
||||||
ConValue::String(v) => v.fmt(f),
|
|
||||||
ConValue::Array(array) => {
|
|
||||||
'['.fmt(f)?;
|
|
||||||
for (idx, element) in array.iter().enumerate() {
|
|
||||||
if idx > 0 {
|
|
||||||
", ".fmt(f)?
|
|
||||||
}
|
|
||||||
element.fmt(f)?
|
|
||||||
}
|
|
||||||
']'.fmt(f)
|
|
||||||
}
|
|
||||||
ConValue::RangeExc(a, b) => write!(f, "{a}..{}", b + 1),
|
|
||||||
ConValue::RangeInc(a, b) => write!(f, "{a}..={b}"),
|
|
||||||
ConValue::Tuple(tuple) => {
|
|
||||||
'('.fmt(f)?;
|
|
||||||
for (idx, element) in tuple.iter().enumerate() {
|
|
||||||
if idx > 0 {
|
|
||||||
", ".fmt(f)?
|
|
||||||
}
|
|
||||||
element.fmt(f)?
|
|
||||||
}
|
|
||||||
')'.fmt(f)
|
|
||||||
}
|
|
||||||
ConValue::Function(func) => {
|
|
||||||
use cl_ast::format::*;
|
|
||||||
use std::fmt::Write;
|
|
||||||
write!(f.pretty(), "{}", func.decl())
|
|
||||||
}
|
|
||||||
ConValue::BuiltIn(func) => {
|
|
||||||
write!(f, "{}", func.description())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod interpret;
|
|
||||||
|
|
||||||
pub mod function {
|
|
||||||
//! Represents a block of code which lives inside the Interpreter
|
|
||||||
use super::{Callable, ConValue, Environment, Error, IResult, Interpret};
|
|
||||||
use cl_ast::{Function as FnDecl, Identifier, Param};
|
|
||||||
/// Represents a block of code which persists inside the Interpreter
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Function {
|
|
||||||
/// Stores the contents of the function declaration
|
|
||||||
decl: Box<FnDecl>,
|
|
||||||
// /// Stores the enclosing scope of the function
|
|
||||||
// env: Box<Environment>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Function {
|
|
||||||
pub fn new(decl: &FnDecl) -> Self {
|
|
||||||
Self { decl: decl.clone().into() }
|
|
||||||
}
|
|
||||||
pub fn decl(&self) -> &FnDecl {
|
|
||||||
&self.decl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Callable for Function {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
let FnDecl { name: Identifier(ref name), .. } = *self.decl;
|
|
||||||
name
|
|
||||||
}
|
|
||||||
fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
|
|
||||||
let FnDecl { name: Identifier(name), args: declargs, body, rety: _ } = &*self.decl;
|
|
||||||
// Check arg mapping
|
|
||||||
if args.len() != declargs.len() {
|
|
||||||
return Err(Error::ArgNumber { want: declargs.len(), got: args.len() });
|
|
||||||
}
|
|
||||||
let Some(body) = body else {
|
|
||||||
return Err(Error::NotDefined(name.into()));
|
|
||||||
};
|
|
||||||
// TODO: completely refactor data storage
|
|
||||||
let mut frame = env.frame("fn args");
|
|
||||||
for (Param { mutability: _, name: Identifier(name), ty: _ }, value) in
|
|
||||||
declargs.iter().zip(args)
|
|
||||||
{
|
|
||||||
frame.insert(name, Some(value.clone()));
|
|
||||||
}
|
|
||||||
match body.interpret(&mut frame) {
|
|
||||||
Err(Error::Return(value)) => Ok(value),
|
|
||||||
Err(Error::Break(value)) => Err(Error::BadBreak(value)),
|
|
||||||
result => result,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod builtin;
|
|
||||||
|
|
||||||
pub mod env {
|
|
||||||
//! Lexical and non-lexical scoping for variables
|
|
||||||
use super::{
|
|
||||||
builtin::{BINARY, MISC, RANGE, UNARY},
|
|
||||||
error::{Error, IResult},
|
|
||||||
function::Function,
|
|
||||||
temp_type_impl::ConValue,
|
|
||||||
BuiltIn, Callable, Interpret,
|
|
||||||
};
|
|
||||||
use cl_ast::{Function as FnDecl, Identifier};
|
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
fmt::Display,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Implements a nested lexical scope
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Environment {
|
|
||||||
frames: Vec<(HashMap<String, Option<ConValue>>, &'static str)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Environment {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
for (frame, name) in self.frames.iter().rev() {
|
|
||||||
writeln!(f, "--- {name} ---")?;
|
|
||||||
for (var, val) in frame {
|
|
||||||
write!(f, "{var}: ")?;
|
|
||||||
match val {
|
|
||||||
Some(value) => writeln!(f, "\t{value}"),
|
|
||||||
None => writeln!(f, "<undefined>"),
|
|
||||||
}?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Default for Environment {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
frames: vec![
|
|
||||||
(to_hashmap(RANGE), "range ops"),
|
|
||||||
(to_hashmap(UNARY), "unary ops"),
|
|
||||||
(to_hashmap(BINARY), "binary ops"),
|
|
||||||
(to_hashmap(MISC), "builtins"),
|
|
||||||
(HashMap::new(), "globals"),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn to_hashmap(from: &[&'static dyn BuiltIn]) -> HashMap<String, Option<ConValue>> {
|
|
||||||
from.iter()
|
|
||||||
.map(|&v| (v.name().into(), Some(v.into())))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Environment {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
/// Creates an [Environment] with no [builtins](super::builtin)
|
|
||||||
pub fn no_builtins(name: &'static str) -> Self {
|
|
||||||
Self { frames: vec![(Default::default(), name)] }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn eval(&mut self, node: &impl Interpret) -> IResult<ConValue> {
|
|
||||||
node.interpret(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calls a function inside the interpreter's scope,
|
|
||||||
/// and returns the result
|
|
||||||
pub fn call(&mut self, name: &str, args: &[ConValue]) -> IResult<ConValue> {
|
|
||||||
// FIXME: Clone to satisfy the borrow checker
|
|
||||||
let function = self.get(name)?.clone();
|
|
||||||
function.call(self, args)
|
|
||||||
}
|
|
||||||
/// Enters a nested scope, returning a [`Frame`] stack-guard.
|
|
||||||
///
|
|
||||||
/// [`Frame`] implements Deref/DerefMut for [`Environment`].
|
|
||||||
pub fn frame(&mut self, name: &'static str) -> Frame {
|
|
||||||
Frame::new(self, name)
|
|
||||||
}
|
|
||||||
/// Resolves a variable mutably.
|
|
||||||
///
|
|
||||||
/// Returns a mutable reference to the variable's record, if it exists.
|
|
||||||
pub fn get_mut(&mut self, id: &str) -> IResult<&mut Option<ConValue>> {
|
|
||||||
for (frame, _) in self.frames.iter_mut().rev() {
|
|
||||||
if let Some(var) = frame.get_mut(id) {
|
|
||||||
return Ok(var);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(Error::NotDefined(id.into()))
|
|
||||||
}
|
|
||||||
/// Resolves a variable immutably.
|
|
||||||
///
|
|
||||||
/// Returns a reference to the variable's contents, if it is defined and initialized.
|
|
||||||
pub fn get(&self, id: &str) -> IResult<&ConValue> {
|
|
||||||
for (frame, _) in self.frames.iter().rev() {
|
|
||||||
match frame.get(id) {
|
|
||||||
Some(Some(var)) => return Ok(var),
|
|
||||||
Some(None) => return Err(Error::NotInitialized(id.into())),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(Error::NotDefined(id.into()))
|
|
||||||
}
|
|
||||||
/// Inserts a new [ConValue] into this [Environment]
|
|
||||||
pub fn insert(&mut self, id: &str, value: Option<ConValue>) {
|
|
||||||
if let Some((frame, _)) = self.frames.last_mut() {
|
|
||||||
frame.insert(id.into(), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A convenience function for registering a [FnDecl] as a [Function]
|
|
||||||
pub fn insert_fn(&mut self, decl: &FnDecl) {
|
|
||||||
let FnDecl { name: Identifier(name), .. } = decl;
|
|
||||||
let (name, function) = (name.clone(), Some(Function::new(decl).into()));
|
|
||||||
if let Some((frame, _)) = self.frames.last_mut() {
|
|
||||||
frame.insert(name, function);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Functions which aid in the implementation of [`Frame`]
|
|
||||||
impl Environment {
|
|
||||||
/// Enters a scope, creating a new namespace for variables
|
|
||||||
fn enter(&mut self, name: &'static str) -> &mut Self {
|
|
||||||
self.frames.push((Default::default(), name));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Exits the scope, destroying all local variables and
|
|
||||||
/// returning the outer scope, if there is one
|
|
||||||
fn exit(&mut self) -> &mut Self {
|
|
||||||
if self.frames.len() > 2 {
|
|
||||||
self.frames.pop();
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a stack frame
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Frame<'scope> {
|
|
||||||
scope: &'scope mut Environment,
|
|
||||||
}
|
|
||||||
impl<'scope> Frame<'scope> {
|
|
||||||
fn new(scope: &'scope mut Environment, name: &'static str) -> Self {
|
|
||||||
Self { scope: scope.enter(name) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'scope> Deref for Frame<'scope> {
|
|
||||||
type Target = Environment;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
self.scope
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'scope> DerefMut for Frame<'scope> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
self.scope
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'scope> Drop for Frame<'scope> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.scope.exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod error {
|
|
||||||
//! The [Error] type represents any error thrown by the [Environment](super::Environment)
|
|
||||||
|
|
||||||
use super::temp_type_impl::ConValue;
|
|
||||||
|
|
||||||
pub type IResult<T> = Result<T, Error>;
|
|
||||||
|
|
||||||
/// Represents any error thrown by the [Environment](super::Environment)
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
/// Propagate a Return value
|
|
||||||
Return(ConValue),
|
|
||||||
/// Propagate a Break value
|
|
||||||
Break(ConValue),
|
|
||||||
/// Break propagated across function bounds
|
|
||||||
BadBreak(ConValue),
|
|
||||||
/// Continue to the next iteration of a loop
|
|
||||||
Continue,
|
|
||||||
/// Underflowed the stack
|
|
||||||
StackUnderflow,
|
|
||||||
/// Exited the last scope
|
|
||||||
ScopeExit,
|
|
||||||
/// Type incompatibility
|
|
||||||
// TODO: store the type information in this error
|
|
||||||
TypeError,
|
|
||||||
/// In clause of For loop didn't yield a Range
|
|
||||||
NotIterable,
|
|
||||||
/// A value could not be indexed
|
|
||||||
NotIndexable,
|
|
||||||
/// An array index went out of bounds
|
|
||||||
OobIndex(usize, usize),
|
|
||||||
/// An expression is not assignable
|
|
||||||
NotAssignable,
|
|
||||||
/// A name was not defined in scope before being used
|
|
||||||
NotDefined(String),
|
|
||||||
/// A name was defined but not initialized
|
|
||||||
NotInitialized(String),
|
|
||||||
/// A value was called, but is not callable
|
|
||||||
NotCallable(ConValue),
|
|
||||||
/// A function was called with the wrong number of arguments
|
|
||||||
ArgNumber {
|
|
||||||
want: usize,
|
|
||||||
got: usize,
|
|
||||||
},
|
|
||||||
NullPointer,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for Error {}
|
|
||||||
impl std::fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Error::Return(value) => write!(f, "return {value}"),
|
|
||||||
Error::Break(value) => write!(f, "break {value}"),
|
|
||||||
Error::BadBreak(value) => write!(f, "rogue break: {value}"),
|
|
||||||
Error::Continue => "continue".fmt(f),
|
|
||||||
Error::StackUnderflow => "Stack underflow".fmt(f),
|
|
||||||
Error::ScopeExit => "Exited the last scope. This is a logic bug.".fmt(f),
|
|
||||||
Error::TypeError => "Incompatible types".fmt(f),
|
|
||||||
Error::NotIterable => "`in` clause of `for` loop did not yield an iterable".fmt(f),
|
|
||||||
Error::NotIndexable => {
|
|
||||||
write!(f, "expression cannot be indexed")
|
|
||||||
}
|
|
||||||
Error::OobIndex(idx, len) => {
|
|
||||||
write!(f, "Index out of bounds: index was {idx}. but len is {len}")
|
|
||||||
}
|
|
||||||
Error::NotAssignable => {
|
|
||||||
write!(f, "expression is not assignable")
|
|
||||||
}
|
|
||||||
Error::NotDefined(value) => {
|
|
||||||
write!(f, "{value} not bound. Did you mean `let {value};`?")
|
|
||||||
}
|
|
||||||
Error::NotInitialized(value) => {
|
|
||||||
write!(f, "{value} bound, but not initialized")
|
|
||||||
}
|
|
||||||
Error::NotCallable(value) => {
|
|
||||||
write!(f, "{value} is not callable.")
|
|
||||||
}
|
|
||||||
Error::ArgNumber { want, got } => {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"Expected {want} argument{}, got {got}",
|
|
||||||
if *want == 1 { "" } else { "s" }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Error::NullPointer => {
|
|
||||||
write!(f, "Attempted to dereference a null pointer?")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
@@ -1,556 +0,0 @@
|
|||||||
//! Converts a text file into tokens
|
|
||||||
#![warn(clippy::all)]
|
|
||||||
#![feature(decl_macro)]
|
|
||||||
use cl_structures::span::Loc;
|
|
||||||
use cl_token::{TokenKind as Kind, *};
|
|
||||||
use std::{
|
|
||||||
iter::Peekable,
|
|
||||||
str::{Chars, FromStr},
|
|
||||||
};
|
|
||||||
use unicode_ident::*;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
|
|
||||||
pub mod lexer_iter {
|
|
||||||
//! Iterator over a [`Lexer`], returning [`LResult<Token>`]s
|
|
||||||
use super::{
|
|
||||||
error::{LResult, Reason},
|
|
||||||
Lexer, Token,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Iterator over a [`Lexer`], returning [`LResult<Token>`]s
|
|
||||||
pub struct LexerIter<'t> {
|
|
||||||
lexer: Lexer<'t>,
|
|
||||||
}
|
|
||||||
impl<'t> Iterator for LexerIter<'t> {
|
|
||||||
type Item = LResult<Token>;
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
match self.lexer.scan() {
|
|
||||||
Ok(v) => Some(Ok(v)),
|
|
||||||
Err(e) => {
|
|
||||||
if e.reason == Reason::EndOfFile {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(Err(e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'t> IntoIterator for Lexer<'t> {
|
|
||||||
type Item = LResult<Token>;
|
|
||||||
type IntoIter = LexerIter<'t>;
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
LexerIter { lexer: self }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The Lexer iterates over the characters in a body of text, searching for [Tokens](Token).
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
/// ```rust
|
|
||||||
/// # use cl_lexer::Lexer;
|
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
/// // Read in your code from somewhere
|
|
||||||
/// let some_code = "
|
|
||||||
/// fn main () {
|
|
||||||
/// // TODO: code goes here!
|
|
||||||
/// }
|
|
||||||
/// ";
|
|
||||||
/// // Create a lexer over your code
|
|
||||||
/// let mut lexer = Lexer::new(some_code);
|
|
||||||
/// // Scan for a single token
|
|
||||||
/// let first_token = lexer.scan()?;
|
|
||||||
/// println!("{first_token:?}");
|
|
||||||
/// // Loop over all the rest of the tokens
|
|
||||||
/// for token in lexer {
|
|
||||||
/// # let token: Result<_,()> = Ok(token?);
|
|
||||||
/// match token {
|
|
||||||
/// Ok(token) => println!("{token:?}"),
|
|
||||||
/// Err(e) => eprintln!("{e:?}"),
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// # Ok(()) }
|
|
||||||
/// ```
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Lexer<'t> {
|
|
||||||
iter: Peekable<Chars<'t>>,
|
|
||||||
start: usize,
|
|
||||||
start_loc: (u32, u32),
|
|
||||||
current: usize,
|
|
||||||
current_loc: (u32, u32),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'t> Lexer<'t> {
|
|
||||||
/// Creates a new [Lexer] over a [str]
|
|
||||||
pub fn new(text: &'t str) -> Self {
|
|
||||||
Self {
|
|
||||||
iter: text.chars().peekable(),
|
|
||||||
start: 0,
|
|
||||||
start_loc: (1, 1),
|
|
||||||
current: 0,
|
|
||||||
current_loc: (1, 1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Scans through the text, searching for the next [Token]
|
|
||||||
pub fn scan(&mut self) -> LResult<Token> {
|
|
||||||
match self.skip_whitespace().peek()? {
|
|
||||||
'{' => self.consume()?.produce_op(Punct::LCurly),
|
|
||||||
'}' => self.consume()?.produce_op(Punct::RCurly),
|
|
||||||
'[' => self.consume()?.produce_op(Punct::LBrack),
|
|
||||||
']' => self.consume()?.produce_op(Punct::RBrack),
|
|
||||||
'(' => self.consume()?.produce_op(Punct::LParen),
|
|
||||||
')' => self.consume()?.produce_op(Punct::RParen),
|
|
||||||
'&' => self.consume()?.amp(),
|
|
||||||
'@' => self.consume()?.produce_op(Punct::At),
|
|
||||||
'\\' => self.consume()?.produce_op(Punct::Backslash),
|
|
||||||
'!' => self.consume()?.bang(),
|
|
||||||
'|' => self.consume()?.bar(),
|
|
||||||
':' => self.consume()?.colon(),
|
|
||||||
',' => self.consume()?.produce_op(Punct::Comma),
|
|
||||||
'.' => self.consume()?.dot(),
|
|
||||||
'=' => self.consume()?.equal(),
|
|
||||||
'`' => self.consume()?.produce_op(Punct::Grave),
|
|
||||||
'>' => self.consume()?.greater(),
|
|
||||||
'#' => self.consume()?.hash(),
|
|
||||||
'<' => self.consume()?.less(),
|
|
||||||
'-' => self.consume()?.minus(),
|
|
||||||
'+' => self.consume()?.plus(),
|
|
||||||
'?' => self.consume()?.produce_op(Punct::Question),
|
|
||||||
'%' => self.consume()?.rem(),
|
|
||||||
';' => self.consume()?.produce_op(Punct::Semi),
|
|
||||||
'/' => self.consume()?.slash(),
|
|
||||||
'*' => self.consume()?.star(),
|
|
||||||
'~' => self.consume()?.produce_op(Punct::Tilde),
|
|
||||||
'^' => self.consume()?.xor(),
|
|
||||||
'0' => self.consume()?.int_with_base(),
|
|
||||||
'1'..='9' => self.digits::<10>(),
|
|
||||||
'"' => self.consume()?.string(),
|
|
||||||
'\'' => self.consume()?.character(),
|
|
||||||
'_' => self.identifier(),
|
|
||||||
i if is_xid_start(i) => self.identifier(),
|
|
||||||
e => {
|
|
||||||
let err = Err(Error::unexpected_char(e, self.line(), self.col()));
|
|
||||||
let _ = self.consume();
|
|
||||||
err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Returns the current line
|
|
||||||
pub fn line(&self) -> u32 {
|
|
||||||
self.start_loc.0
|
|
||||||
}
|
|
||||||
/// Returns the current column
|
|
||||||
pub fn col(&self) -> u32 {
|
|
||||||
self.start_loc.1
|
|
||||||
}
|
|
||||||
fn next(&mut self) -> LResult<char> {
|
|
||||||
let out = self.peek();
|
|
||||||
self.consume()?;
|
|
||||||
out
|
|
||||||
}
|
|
||||||
fn peek(&mut self) -> LResult<char> {
|
|
||||||
self.iter
|
|
||||||
.peek()
|
|
||||||
.copied()
|
|
||||||
.ok_or(Error::end_of_file(self.line(), self.col()))
|
|
||||||
}
|
|
||||||
fn produce(&mut self, kind: TokenKind, data: impl Into<TokenData>) -> LResult<Token> {
|
|
||||||
let loc = self.start_loc;
|
|
||||||
self.start_loc = self.current_loc;
|
|
||||||
self.start = self.current;
|
|
||||||
Ok(Token::new(kind, data, loc.0, loc.1))
|
|
||||||
}
|
|
||||||
fn produce_op(&mut self, kind: Punct) -> LResult<Token> {
|
|
||||||
self.produce(TokenKind::Punct(kind), ())
|
|
||||||
}
|
|
||||||
fn skip_whitespace(&mut self) -> &mut Self {
|
|
||||||
while let Ok(c) = self.peek() {
|
|
||||||
if !c.is_whitespace() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let _ = self.consume();
|
|
||||||
}
|
|
||||||
self.start = self.current;
|
|
||||||
self.start_loc = self.current_loc;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
fn consume(&mut self) -> LResult<&mut Self> {
|
|
||||||
self.current += 1;
|
|
||||||
match self.iter.next() {
|
|
||||||
Some('\n') => {
|
|
||||||
let (line, col) = &mut self.current_loc;
|
|
||||||
*line += 1;
|
|
||||||
*col = 1;
|
|
||||||
}
|
|
||||||
Some(_) => self.current_loc.1 += 1,
|
|
||||||
None => Err(Error::end_of_file(self.line(), self.col()))?,
|
|
||||||
}
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Digraphs and trigraphs
|
|
||||||
impl<'t> Lexer<'t> {
|
|
||||||
fn amp(&mut self) -> LResult<Token> {
|
|
||||||
match self.peek() {
|
|
||||||
Ok('&') => self.consume()?.produce_op(Punct::AmpAmp),
|
|
||||||
Ok('=') => self.consume()?.produce_op(Punct::AmpEq),
|
|
||||||
_ => self.produce_op(Punct::Amp),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn bang(&mut self) -> LResult<Token> {
|
|
||||||
match self.peek() {
|
|
||||||
Ok('!') => self.consume()?.produce_op(Punct::BangBang),
|
|
||||||
Ok('=') => self.consume()?.produce_op(Punct::BangEq),
|
|
||||||
_ => self.produce_op(Punct::Bang),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn bar(&mut self) -> LResult<Token> {
|
|
||||||
match self.peek() {
|
|
||||||
Ok('|') => self.consume()?.produce_op(Punct::BarBar),
|
|
||||||
Ok('=') => self.consume()?.produce_op(Punct::BarEq),
|
|
||||||
_ => self.produce_op(Punct::Bar),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn colon(&mut self) -> LResult<Token> {
|
|
||||||
match self.peek() {
|
|
||||||
Ok(':') => self.consume()?.produce_op(Punct::ColonColon),
|
|
||||||
_ => self.produce_op(Punct::Colon),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn dot(&mut self) -> LResult<Token> {
|
|
||||||
match self.peek() {
|
|
||||||
Ok('.') => {
|
|
||||||
if let Ok('=') = self.consume()?.peek() {
|
|
||||||
self.consume()?.produce_op(Punct::DotDotEq)
|
|
||||||
} else {
|
|
||||||
self.produce_op(Punct::DotDot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => self.produce_op(Punct::Dot),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn equal(&mut self) -> LResult<Token> {
|
|
||||||
match self.peek() {
|
|
||||||
Ok('=') => self.consume()?.produce_op(Punct::EqEq),
|
|
||||||
Ok('>') => self.consume()?.produce_op(Punct::FatArrow),
|
|
||||||
_ => self.produce_op(Punct::Eq),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn greater(&mut self) -> LResult<Token> {
|
|
||||||
match self.peek() {
|
|
||||||
Ok('=') => self.consume()?.produce_op(Punct::GtEq),
|
|
||||||
Ok('>') => {
|
|
||||||
if let Ok('=') = self.consume()?.peek() {
|
|
||||||
self.consume()?.produce_op(Punct::GtGtEq)
|
|
||||||
} else {
|
|
||||||
self.produce_op(Punct::GtGt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => self.produce_op(Punct::Gt),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn hash(&mut self) -> LResult<Token> {
|
|
||||||
match self.peek() {
|
|
||||||
Ok('!') => self.consume()?.produce_op(Punct::HashBang),
|
|
||||||
_ => self.produce_op(Punct::Hash),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn less(&mut self) -> LResult<Token> {
|
|
||||||
match self.peek() {
|
|
||||||
Ok('=') => self.consume()?.produce_op(Punct::LtEq),
|
|
||||||
Ok('<') => {
|
|
||||||
if let Ok('=') = self.consume()?.peek() {
|
|
||||||
self.consume()?.produce_op(Punct::LtLtEq)
|
|
||||||
} else {
|
|
||||||
self.produce_op(Punct::LtLt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => self.produce_op(Punct::Lt),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn minus(&mut self) -> LResult<Token> {
|
|
||||||
match self.peek() {
|
|
||||||
Ok('=') => self.consume()?.produce_op(Punct::MinusEq),
|
|
||||||
Ok('>') => self.consume()?.produce_op(Punct::Arrow),
|
|
||||||
_ => self.produce_op(Punct::Minus),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn plus(&mut self) -> LResult<Token> {
|
|
||||||
match self.peek() {
|
|
||||||
Ok('=') => self.consume()?.produce_op(Punct::PlusEq),
|
|
||||||
_ => self.produce_op(Punct::Plus),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn rem(&mut self) -> LResult<Token> {
|
|
||||||
match self.peek() {
|
|
||||||
Ok('=') => self.consume()?.produce_op(Punct::RemEq),
|
|
||||||
_ => self.produce_op(Punct::Rem),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn slash(&mut self) -> LResult<Token> {
|
|
||||||
match self.peek() {
|
|
||||||
Ok('=') => self.consume()?.produce_op(Punct::SlashEq),
|
|
||||||
Ok('/') => self.consume()?.line_comment(),
|
|
||||||
Ok('*') => self.consume()?.block_comment(),
|
|
||||||
_ => self.produce_op(Punct::Slash),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn star(&mut self) -> LResult<Token> {
|
|
||||||
match self.peek() {
|
|
||||||
Ok('=') => self.consume()?.produce_op(Punct::StarEq),
|
|
||||||
_ => self.produce_op(Punct::Star),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn xor(&mut self) -> LResult<Token> {
|
|
||||||
match self.peek() {
|
|
||||||
Ok('=') => self.consume()?.produce_op(Punct::XorEq),
|
|
||||||
Ok('^') => self.consume()?.produce_op(Punct::XorXor),
|
|
||||||
_ => self.produce_op(Punct::Xor),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Comments
|
|
||||||
impl<'t> Lexer<'t> {
|
|
||||||
fn line_comment(&mut self) -> LResult<Token> {
|
|
||||||
while Ok('\n') != self.peek() {
|
|
||||||
self.consume()?;
|
|
||||||
}
|
|
||||||
self.produce(Kind::Comment, ())
|
|
||||||
}
|
|
||||||
fn block_comment(&mut self) -> LResult<Token> {
|
|
||||||
while let Ok(c) = self.next() {
|
|
||||||
if '*' == c && Ok('/') == self.next() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.produce(Kind::Comment, ())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Identifiers
|
|
||||||
impl<'t> Lexer<'t> {
|
|
||||||
fn identifier(&mut self) -> LResult<Token> {
|
|
||||||
let mut out = String::from(self.xid_start()?);
|
|
||||||
while let Ok(c) = self.xid_continue() {
|
|
||||||
out.push(c)
|
|
||||||
}
|
|
||||||
if let Ok(keyword) = Kind::from_str(&out) {
|
|
||||||
self.produce(keyword, ())
|
|
||||||
} else {
|
|
||||||
self.produce(Kind::Identifier, TokenData::String(out))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn xid_start(&mut self) -> LResult<char> {
|
|
||||||
match self.peek()? {
|
|
||||||
xid if xid == '_' || is_xid_start(xid) => {
|
|
||||||
self.consume()?;
|
|
||||||
Ok(xid)
|
|
||||||
}
|
|
||||||
bad => Err(Error::not_identifier(bad, self.line(), self.col())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn xid_continue(&mut self) -> LResult<char> {
|
|
||||||
match self.peek()? {
|
|
||||||
xid if is_xid_continue(xid) => {
|
|
||||||
self.consume()?;
|
|
||||||
Ok(xid)
|
|
||||||
}
|
|
||||||
bad => Err(Error::not_identifier(bad, self.line(), self.col())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Integers
|
|
||||||
impl<'t> Lexer<'t> {
|
|
||||||
fn int_with_base(&mut self) -> LResult<Token> {
|
|
||||||
match self.peek() {
|
|
||||||
Ok('x') => self.consume()?.digits::<16>(),
|
|
||||||
Ok('d') => self.consume()?.digits::<10>(),
|
|
||||||
Ok('o') => self.consume()?.digits::<8>(),
|
|
||||||
Ok('b') => self.consume()?.digits::<2>(),
|
|
||||||
Ok('0'..='9') => self.digits::<10>(),
|
|
||||||
_ => self.produce(Kind::Literal, 0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn digits<const B: u32>(&mut self) -> LResult<Token> {
|
|
||||||
let mut value = self.digit::<B>()? as u128;
|
|
||||||
while let Ok(true) = self.peek().as_ref().map(char::is_ascii_alphanumeric) {
|
|
||||||
value = value * B as u128 + self.digit::<B>()? as u128;
|
|
||||||
}
|
|
||||||
self.produce(Kind::Literal, value)
|
|
||||||
}
|
|
||||||
fn digit<const B: u32>(&mut self) -> LResult<u32> {
|
|
||||||
let digit = self.peek()?;
|
|
||||||
self.consume()?;
|
|
||||||
digit
|
|
||||||
.to_digit(B)
|
|
||||||
.ok_or(Error::invalid_digit(digit, self.line(), self.col()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Strings and characters
|
|
||||||
impl<'t> Lexer<'t> {
|
|
||||||
fn string(&mut self) -> LResult<Token> {
|
|
||||||
let mut value = String::new();
|
|
||||||
while '"'
|
|
||||||
!= self
|
|
||||||
.peek()
|
|
||||||
.map_err(|e| e.mask_reason(Reason::UnmatchedDelimiters('"')))?
|
|
||||||
{
|
|
||||||
value.push(self.unescape()?)
|
|
||||||
}
|
|
||||||
self.consume()?.produce(Kind::Literal, value)
|
|
||||||
}
|
|
||||||
fn character(&mut self) -> LResult<Token> {
|
|
||||||
let out = self.unescape()?;
|
|
||||||
match self.peek()? {
|
|
||||||
'\'' => self.consume()?.produce(Kind::Literal, out),
|
|
||||||
_ => Err(Error::unmatched_delimiters('\'', self.line(), self.col())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Unescape a single character
|
|
||||||
fn unescape(&mut self) -> LResult<char> {
|
|
||||||
match self.next() {
|
|
||||||
Ok('\\') => (),
|
|
||||||
other => return other,
|
|
||||||
}
|
|
||||||
Ok(match self.next()? {
|
|
||||||
'a' => '\x07',
|
|
||||||
'b' => '\x08',
|
|
||||||
'f' => '\x0c',
|
|
||||||
'n' => '\n',
|
|
||||||
'r' => '\r',
|
|
||||||
't' => '\t',
|
|
||||||
'x' => self.hex_escape()?,
|
|
||||||
'u' => self.unicode_escape()?,
|
|
||||||
'0' => '\0',
|
|
||||||
chr => chr,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/// unescape a single 2-digit hex escape
|
|
||||||
fn hex_escape(&mut self) -> LResult<char> {
|
|
||||||
let out = (self.digit::<16>()? << 4) + self.digit::<16>()?;
|
|
||||||
char::from_u32(out).ok_or(Error::bad_unicode(out, self.line(), self.col()))
|
|
||||||
}
|
|
||||||
/// unescape a single \u{} unicode escape
|
|
||||||
fn unicode_escape(&mut self) -> LResult<char> {
|
|
||||||
let mut out = 0;
|
|
||||||
let Ok('{') = self.peek() else {
|
|
||||||
return Err(Error::invalid_escape('u', self.line(), self.col()));
|
|
||||||
};
|
|
||||||
self.consume()?;
|
|
||||||
while let Ok(c) = self.peek() {
|
|
||||||
match c {
|
|
||||||
'}' => {
|
|
||||||
self.consume()?;
|
|
||||||
return char::from_u32(out).ok_or(Error::bad_unicode(
|
|
||||||
out,
|
|
||||||
self.line(),
|
|
||||||
self.col(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
_ => out = (out << 4) + self.digit::<16>()?,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(Error::invalid_escape('u', self.line(), self.col()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'t> From<&Lexer<'t>> for Loc {
|
|
||||||
fn from(value: &Lexer<'t>) -> Self {
|
|
||||||
Loc(value.line(), value.col())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use error::{Error, LResult, Reason};
|
|
||||||
pub mod error {
|
|
||||||
//! [Error] type for the [Lexer](super::Lexer)
|
|
||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
/// Result type with [Err] = [Error]
|
|
||||||
pub type LResult<T> = Result<T, Error>;
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Error {
|
|
||||||
pub reason: Reason,
|
|
||||||
pub line: u32,
|
|
||||||
pub col: u32,
|
|
||||||
}
|
|
||||||
/// The reason for the [Error]
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub enum Reason {
|
|
||||||
/// Found an opening delimiter of type [char], but not the expected closing delimiter
|
|
||||||
UnmatchedDelimiters(char),
|
|
||||||
/// Found a character that doesn't belong to any [TokenKind](cl_token::TokenKind)
|
|
||||||
UnexpectedChar(char),
|
|
||||||
/// Found a character that's not valid in identifiers while looking for an identifier
|
|
||||||
NotIdentifier(char),
|
|
||||||
/// Found a character that's not valid in an escape sequence while looking for an escape
|
|
||||||
/// sequence
|
|
||||||
UnknownEscape(char),
|
|
||||||
/// Escape sequence contains invalid hexadecimal digit or unmatched braces
|
|
||||||
InvalidEscape(char),
|
|
||||||
/// Character is not a valid digit in the requested base
|
|
||||||
InvalidDigit(char),
|
|
||||||
/// Base conversion requested, but the base character was not in the set of known
|
|
||||||
/// characters
|
|
||||||
UnknownBase(char),
|
|
||||||
/// Unicode escape does not map to a valid unicode code-point
|
|
||||||
BadUnicode(u32),
|
|
||||||
/// Reached end of input
|
|
||||||
EndOfFile,
|
|
||||||
}
|
|
||||||
error_impl! {
|
|
||||||
unmatched_delimiters(c: char) => Reason::UnmatchedDelimiters(c),
|
|
||||||
unexpected_char(c: char) => Reason::UnexpectedChar(c),
|
|
||||||
not_identifier(c: char) => Reason::NotIdentifier(c),
|
|
||||||
unknown_escape(e: char) => Reason::UnknownEscape(e),
|
|
||||||
invalid_escape(e: char) => Reason::InvalidEscape(e),
|
|
||||||
invalid_digit(digit: char) => Reason::InvalidDigit(digit),
|
|
||||||
unknown_base(base: char) => Reason::UnknownBase(base),
|
|
||||||
bad_unicode(value: u32) => Reason::BadUnicode(value),
|
|
||||||
end_of_file => Reason::EndOfFile,
|
|
||||||
}
|
|
||||||
impl Error {
|
|
||||||
/// Changes the [Reason] of this error
|
|
||||||
pub(super) fn mask_reason(self, reason: Reason) -> Self {
|
|
||||||
Self { reason, ..self }
|
|
||||||
}
|
|
||||||
/// Returns the [Reason] for this error
|
|
||||||
pub fn reason(&self) -> &Reason {
|
|
||||||
&self.reason
|
|
||||||
}
|
|
||||||
/// Returns the (line, col) where the error happened
|
|
||||||
pub fn location(&self) -> (u32, u32) {
|
|
||||||
(self.line, self.col)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
macro error_impl ($($fn:ident$(( $($p:ident: $t:ty),* ))? => $reason:expr),*$(,)?) {
|
|
||||||
#[allow(dead_code)]
|
|
||||||
impl Error {
|
|
||||||
$(pub(super) fn $fn ($($($p: $t),*,)? line: u32, col: u32) -> Self {
|
|
||||||
Self { reason: $reason, line, col }
|
|
||||||
})*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl std::error::Error for Error {}
|
|
||||||
impl Display for Error {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{}:{}: {}", self.line, self.col, self.reason)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Reason {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Reason::UnmatchedDelimiters(c) => write! {f, "Unmatched `{c}` in input"},
|
|
||||||
Reason::UnexpectedChar(c) => write!(f, "Character `{c}` not expected"),
|
|
||||||
Reason::NotIdentifier(c) => write!(f, "Character `{c}` not valid in identifiers"),
|
|
||||||
Reason::UnknownEscape(c) => write!(f, "`\\{c}` is not a known escape sequence"),
|
|
||||||
Reason::InvalidEscape(c) => write!(f, "Escape sequence `\\{c}`... is malformed"),
|
|
||||||
Reason::InvalidDigit(c) => write!(f, "`{c}` is not a valid digit"),
|
|
||||||
Reason::UnknownBase(c) => write!(f, "`0{c}`... is not a valid base"),
|
|
||||||
Reason::BadUnicode(c) => write!(f, "`{c}` is not a valid unicode code-point"),
|
|
||||||
Reason::EndOfFile => write!(f, "Reached end of input"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,109 +0,0 @@
|
|||||||
use cl_lexer::Lexer;
|
|
||||||
use cl_parser::Parser;
|
|
||||||
use cl_repl::repline::{error::Error as RlError, Repline};
|
|
||||||
use cl_typeck::{name_collector::NameCollector, project::Project};
|
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
|
||||||
let mut prj = Project::default();
|
|
||||||
let mut tcol = NameCollector::new(&mut prj);
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"--- {} v{} 💪🦈 ---",
|
|
||||||
env!("CARGO_BIN_NAME"),
|
|
||||||
env!("CARGO_PKG_VERSION"),
|
|
||||||
);
|
|
||||||
|
|
||||||
read_and(
|
|
||||||
"\x1b[33m",
|
|
||||||
"cl>",
|
|
||||||
"? >",
|
|
||||||
|line| -> Result<_, Box<dyn Error>> {
|
|
||||||
if line.trim_start().is_empty() {
|
|
||||||
query(&tcol)?;
|
|
||||||
return Ok(Response::Deny);
|
|
||||||
}
|
|
||||||
let mut parser = Parser::new(Lexer::new(line));
|
|
||||||
let code = match parser.file() {
|
|
||||||
Ok(code) => code,
|
|
||||||
Err(e) => Err(e)?,
|
|
||||||
};
|
|
||||||
tcol.file(&code)?;
|
|
||||||
Ok(Response::Accept)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Response {
|
|
||||||
Accept,
|
|
||||||
Deny,
|
|
||||||
Break,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_and(
|
|
||||||
color: &str,
|
|
||||||
begin: &str,
|
|
||||||
again: &str,
|
|
||||||
mut f: impl FnMut(&str) -> Result<Response, Box<dyn Error>>,
|
|
||||||
) -> Result<(), Box<dyn Error>> {
|
|
||||||
let mut rl = Repline::new(color, begin, again);
|
|
||||||
loop {
|
|
||||||
let line = match rl.read() {
|
|
||||||
Err(RlError::CtrlC(_)) => break,
|
|
||||||
Err(RlError::CtrlD(line)) => {
|
|
||||||
rl.deny();
|
|
||||||
line
|
|
||||||
}
|
|
||||||
Ok(line) => line,
|
|
||||||
Err(e) => Err(e)?,
|
|
||||||
};
|
|
||||||
print!("\x1b[G\x1b[J");
|
|
||||||
match f(&line) {
|
|
||||||
Ok(Response::Accept) => rl.accept(),
|
|
||||||
Ok(Response::Deny) => rl.deny(),
|
|
||||||
Ok(Response::Break) => break,
|
|
||||||
Err(e) => print!("\x1b[40G\x1bJ\x1b[91m{e}\x1b[0m"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn query(prj: &Project) -> Result<(), Box<dyn Error>> {
|
|
||||||
use cl_typeck::{
|
|
||||||
definition::{Def, DefKind},
|
|
||||||
type_kind::TypeKind,
|
|
||||||
};
|
|
||||||
read_and("\x1b[35m", "qy>", "? >", |line| {
|
|
||||||
if line.trim_start().is_empty() {
|
|
||||||
return Ok(Response::Break);
|
|
||||||
}
|
|
||||||
match line {
|
|
||||||
"$all\n" => println!("{prj:#?}"),
|
|
||||||
_ => {
|
|
||||||
// parse it as a path, and convert the path into a borrowed path
|
|
||||||
let path = Parser::new(Lexer::new(line)).path()?;
|
|
||||||
|
|
||||||
let Some((type_id, path)) = prj.get_type((&path).into(), prj.module_root) else {
|
|
||||||
return Ok(Response::Deny);
|
|
||||||
};
|
|
||||||
let Def { name, vis, meta: _, kind, source: _, module } = &prj[type_id];
|
|
||||||
match (kind, prj.get_value(path, type_id)) {
|
|
||||||
(_, Some((val, path))) => {
|
|
||||||
println!("value {}; {path}\n{:#?}", usize::from(val), prj[val])
|
|
||||||
}
|
|
||||||
(DefKind::Type(TypeKind::Module), None) => println!(
|
|
||||||
"{vis}mod \"{name}\" (#{}); {path}\n{:#?}",
|
|
||||||
usize::from(type_id),
|
|
||||||
module
|
|
||||||
),
|
|
||||||
(_, None) => println!(
|
|
||||||
"type {name}(#{}); {path}\n{:#?}",
|
|
||||||
usize::from(type_id),
|
|
||||||
prj.pool[type_id]
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Response::Accept)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
use cl_repl::{cli::run, tools::is_terminal};
|
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
|
||||||
if is_terminal() {
|
|
||||||
println!(
|
|
||||||
"--- {} v{} 💪🦈 ---",
|
|
||||||
env!("CARGO_BIN_NAME"),
|
|
||||||
env!("CARGO_PKG_VERSION"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
run(argh::from_env())
|
|
||||||
}
|
|
||||||
@@ -1,462 +0,0 @@
|
|||||||
//! Utilities for cl-frontend
|
|
||||||
//!
|
|
||||||
//! # TODO
|
|
||||||
//! - [ ] Readline-like line editing
|
|
||||||
//! - [ ] Raw mode?
|
|
||||||
#![warn(clippy::all)]
|
|
||||||
|
|
||||||
pub mod ansi {
|
|
||||||
// ANSI color escape sequences
|
|
||||||
pub const ANSI_RED: &str = "\x1b[31m";
|
|
||||||
pub const ANSI_GREEN: &str = "\x1b[32m"; // the color of type checker mode
|
|
||||||
pub const ANSI_CYAN: &str = "\x1b[36m";
|
|
||||||
// pub const ANSI_BRIGHT_GREEN: &str = "\x1b[92m";
|
|
||||||
pub const ANSI_BRIGHT_BLUE: &str = "\x1b[94m";
|
|
||||||
pub const ANSI_BRIGHT_MAGENTA: &str = "\x1b[95m";
|
|
||||||
// const ANSI_BRIGHT_CYAN: &str = "\x1b[96m";
|
|
||||||
pub const ANSI_RESET: &str = "\x1b[0m";
|
|
||||||
pub const ANSI_OUTPUT: &str = "\x1b[38;5;117m";
|
|
||||||
|
|
||||||
pub const ANSI_CLEAR_LINES: &str = "\x1b[G\x1b[J";
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod args {
|
|
||||||
use crate::tools::is_terminal;
|
|
||||||
use argh::FromArgs;
|
|
||||||
use std::{path::PathBuf, str::FromStr};
|
|
||||||
|
|
||||||
/// The Conlang prototype debug interface
|
|
||||||
#[derive(Clone, Debug, FromArgs, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub struct Args {
|
|
||||||
/// the main source file
|
|
||||||
#[argh(positional)]
|
|
||||||
pub file: Option<PathBuf>,
|
|
||||||
|
|
||||||
/// files to include
|
|
||||||
#[argh(option, short = 'I')]
|
|
||||||
pub include: Vec<PathBuf>,
|
|
||||||
|
|
||||||
/// the Repl mode to start in
|
|
||||||
#[argh(option, short = 'm', default = "Default::default()")]
|
|
||||||
pub mode: Mode,
|
|
||||||
|
|
||||||
/// whether to start the repl (`true` or `false`)
|
|
||||||
#[argh(option, short = 'r', default = "is_terminal()")]
|
|
||||||
pub repl: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The CLI's operating mode
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub enum Mode {
|
|
||||||
Tokenize,
|
|
||||||
Beautify,
|
|
||||||
#[default]
|
|
||||||
Interpret,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mode {
|
|
||||||
pub fn ansi_color(self) -> &'static str {
|
|
||||||
use super::ansi::*;
|
|
||||||
match self {
|
|
||||||
Mode::Tokenize => ANSI_BRIGHT_BLUE,
|
|
||||||
Mode::Beautify => ANSI_BRIGHT_MAGENTA,
|
|
||||||
// Mode::Resolve => ANSI_GREEN,
|
|
||||||
Mode::Interpret => ANSI_CYAN,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Mode {
|
|
||||||
type Err = &'static str;
|
|
||||||
fn from_str(s: &str) -> Result<Self, &'static str> {
|
|
||||||
Ok(match s {
|
|
||||||
"i" | "interpret" | "r" | "run" => Mode::Interpret,
|
|
||||||
"b" | "beautify" | "p" | "pretty" => Mode::Beautify,
|
|
||||||
// "r" | "resolve" | "typecheck" | "type" => Mode::Resolve,
|
|
||||||
"t" | "tokenize" | "token" => Mode::Tokenize,
|
|
||||||
_ => Err("Recognized modes are: 'r' \"run\", 'p' \"pretty\", 't' \"token\"")?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod program {
|
|
||||||
use cl_interpret::{
|
|
||||||
env::Environment, error::IResult, interpret::Interpret, temp_type_impl::ConValue,
|
|
||||||
};
|
|
||||||
|
|
||||||
use cl_ast::{self as ast, format::*};
|
|
||||||
use cl_lexer::Lexer;
|
|
||||||
use cl_parser::{error::PResult, Parser};
|
|
||||||
// use conlang::resolver::{error::TyResult, Resolver};
|
|
||||||
use std::{fmt::Display, io::Write};
|
|
||||||
|
|
||||||
pub struct Parsable;
|
|
||||||
|
|
||||||
pub enum Parsed {
|
|
||||||
File(ast::File),
|
|
||||||
Stmt(ast::Stmt),
|
|
||||||
Expr(ast::Expr),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Program<'t, Variant> {
|
|
||||||
text: &'t str,
|
|
||||||
data: Variant,
|
|
||||||
}
|
|
||||||
impl<'t, V> Program<'t, V> {
|
|
||||||
pub fn lex(&self) -> Lexer {
|
|
||||||
Lexer::new(self.text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'t> Program<'t, Parsable> {
|
|
||||||
pub fn new(text: &'t str) -> Self {
|
|
||||||
Self { text, data: Parsable }
|
|
||||||
}
|
|
||||||
pub fn parse(self) -> PResult<Program<'t, Parsed>> {
|
|
||||||
self.parse_file().or_else(|_| self.parse_stmt())
|
|
||||||
}
|
|
||||||
pub fn parse_expr(&self) -> PResult<Program<'t, Parsed>> {
|
|
||||||
Ok(Program { data: Parsed::Expr(Parser::new(self.lex()).expr()?), text: self.text })
|
|
||||||
}
|
|
||||||
pub fn parse_stmt(&self) -> PResult<Program<'t, Parsed>> {
|
|
||||||
Ok(Program { data: Parsed::Stmt(Parser::new(self.lex()).stmt()?), text: self.text })
|
|
||||||
}
|
|
||||||
pub fn parse_file(&self) -> PResult<Program<'t, Parsed>> {
|
|
||||||
Ok(Program { data: Parsed::File(Parser::new(self.lex()).file()?), text: self.text })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'t> Program<'t, Parsed> {
|
|
||||||
pub fn debug(&self) {
|
|
||||||
match &self.data {
|
|
||||||
Parsed::File(v) => eprintln!("{v:?}"),
|
|
||||||
Parsed::Stmt(v) => eprintln!("{v:?}"),
|
|
||||||
Parsed::Expr(v) => eprintln!("{v:?}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn print(&self) {
|
|
||||||
let mut f = std::io::stdout().pretty();
|
|
||||||
let _ = match &self.data {
|
|
||||||
Parsed::File(v) => writeln!(f, "{v}"),
|
|
||||||
Parsed::Stmt(v) => writeln!(f, "{v}"),
|
|
||||||
Parsed::Expr(v) => writeln!(f, "{v}"),
|
|
||||||
};
|
|
||||||
// println!("{self}")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(&self, env: &mut Environment) -> IResult<ConValue> {
|
|
||||||
match &self.data {
|
|
||||||
Parsed::File(v) => v.interpret(env),
|
|
||||||
Parsed::Stmt(v) => v.interpret(env),
|
|
||||||
Parsed::Expr(v) => v.interpret(env),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub fn resolve(&mut self, resolver: &mut Resolver) -> TyResult<()> {
|
|
||||||
// match &mut self.data {
|
|
||||||
// Parsed::Program(start) => start.resolve(resolver),
|
|
||||||
// Parsed::Expr(expr) => expr.resolve(resolver),
|
|
||||||
// }
|
|
||||||
// .map(|ty| println!("{ty}"))
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'t> Display for Program<'t, Parsed> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match &self.data {
|
|
||||||
Parsed::File(v) => write!(f, "{v}"),
|
|
||||||
Parsed::Stmt(v) => write!(f, "{v}"),
|
|
||||||
Parsed::Expr(v) => write!(f, "{v}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod cli {
|
|
||||||
//! Implement's the command line interface
|
|
||||||
use crate::{
|
|
||||||
args::{Args, Mode},
|
|
||||||
program::{Parsable, Program},
|
|
||||||
repl::Repl,
|
|
||||||
tools::print_token,
|
|
||||||
};
|
|
||||||
use cl_interpret::{env::Environment, temp_type_impl::ConValue};
|
|
||||||
use cl_lexer::Lexer;
|
|
||||||
use cl_parser::Parser;
|
|
||||||
use std::{error::Error, path::Path};
|
|
||||||
|
|
||||||
/// Run the command line interface
|
|
||||||
pub fn run(args: Args) -> Result<(), Box<dyn Error>> {
|
|
||||||
let Args { file, include, mode, repl } = args;
|
|
||||||
|
|
||||||
let mut env = Environment::new();
|
|
||||||
for path in include {
|
|
||||||
load_file(&mut env, path)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if repl {
|
|
||||||
if let Some(file) = file {
|
|
||||||
load_file(&mut env, file)?;
|
|
||||||
}
|
|
||||||
Repl::with_env(mode, env).repl()
|
|
||||||
} else {
|
|
||||||
let code = match &file {
|
|
||||||
Some(file) => std::fs::read_to_string(file)?,
|
|
||||||
None => std::io::read_to_string(std::io::stdin())?,
|
|
||||||
};
|
|
||||||
let code = Program::new(&code);
|
|
||||||
|
|
||||||
match mode {
|
|
||||||
Mode::Tokenize => tokenize(code, file),
|
|
||||||
Mode::Beautify => beautify(code),
|
|
||||||
Mode::Interpret => interpret(code, &mut env),
|
|
||||||
}?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_file(
|
|
||||||
env: &mut Environment,
|
|
||||||
path: impl AsRef<Path>,
|
|
||||||
) -> Result<ConValue, Box<dyn Error>> {
|
|
||||||
let file = std::fs::read_to_string(path)?;
|
|
||||||
let code = Parser::new(Lexer::new(&file)).file()?;
|
|
||||||
env.eval(&code).map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tokenize(
|
|
||||||
code: Program<Parsable>,
|
|
||||||
path: Option<impl AsRef<Path>>,
|
|
||||||
) -> Result<(), Box<dyn Error>> {
|
|
||||||
for token in code.lex() {
|
|
||||||
if let Some(ref path) = path {
|
|
||||||
print!("{}:", path.as_ref().display());
|
|
||||||
}
|
|
||||||
match token {
|
|
||||||
Ok(token) => print_token(&token),
|
|
||||||
Err(e) => println!("{e}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn beautify(code: Program<Parsable>) -> Result<(), Box<dyn Error>> {
|
|
||||||
code.parse()?.print();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn interpret(code: Program<Parsable>, env: &mut Environment) -> Result<(), Box<dyn Error>> {
|
|
||||||
match code.parse()?.run(env)? {
|
|
||||||
ConValue::Empty => {}
|
|
||||||
ret => println!("{ret}"),
|
|
||||||
}
|
|
||||||
if env.get("main").is_ok() {
|
|
||||||
match env.call("main", &[])? {
|
|
||||||
ConValue::Empty => {}
|
|
||||||
ret => println!("{ret}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod repl {
|
|
||||||
use crate::{
|
|
||||||
ansi::*,
|
|
||||||
args::Mode,
|
|
||||||
program::{Parsable, Parsed, Program},
|
|
||||||
tools::print_token,
|
|
||||||
};
|
|
||||||
use cl_interpret::{env::Environment, temp_type_impl::ConValue};
|
|
||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
/// Implements the interactive interpreter
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Repl {
|
|
||||||
prompt_again: &'static str, // " ?>"
|
|
||||||
prompt_begin: &'static str, // "cl>"
|
|
||||||
prompt_error: &'static str, // "! >"
|
|
||||||
prompt_succs: &'static str, // " ->"
|
|
||||||
env: Environment,
|
|
||||||
mode: Mode,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Repl {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
prompt_begin: "cl>",
|
|
||||||
prompt_again: " ?>",
|
|
||||||
prompt_error: "! >",
|
|
||||||
prompt_succs: " =>",
|
|
||||||
env: Default::default(),
|
|
||||||
mode: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prompt functions
|
|
||||||
impl Repl {
|
|
||||||
pub fn prompt_result<T: Display, E: Display>(&self, res: Result<T, E>) {
|
|
||||||
match &res {
|
|
||||||
Ok(v) => self.prompt_succs(v),
|
|
||||||
Err(e) => self.prompt_error(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn prompt_error(&self, err: &impl Display) {
|
|
||||||
let Self { prompt_error: prompt, .. } = self;
|
|
||||||
println!("{ANSI_CLEAR_LINES}{ANSI_RED}{prompt} {err}{ANSI_RESET}")
|
|
||||||
}
|
|
||||||
pub fn prompt_succs(&self, value: &impl Display) {
|
|
||||||
let Self { prompt_succs: _prompt, .. } = self;
|
|
||||||
println!("{ANSI_GREEN}{value}{ANSI_RESET}")
|
|
||||||
}
|
|
||||||
/// Resets the cursor to the start of the line, clears the terminal,
|
|
||||||
/// and sets the output color
|
|
||||||
pub fn begin_output(&self) {
|
|
||||||
print!("{ANSI_CLEAR_LINES}{ANSI_OUTPUT}")
|
|
||||||
}
|
|
||||||
pub fn clear_line(&self) {}
|
|
||||||
}
|
|
||||||
/// The actual REPL
|
|
||||||
impl Repl {
|
|
||||||
/// Constructs a new [Repl] with the provided [Mode]
|
|
||||||
pub fn new(mode: Mode) -> Self {
|
|
||||||
Self { mode, ..Default::default() }
|
|
||||||
}
|
|
||||||
/// Constructs a new [Repl] with the provided [Mode] and [Environment]
|
|
||||||
pub fn with_env(mode: Mode, env: Environment) -> Self {
|
|
||||||
Self { mode, env, ..Default::default() }
|
|
||||||
}
|
|
||||||
/// Runs the main REPL loop
|
|
||||||
pub fn repl(&mut self) {
|
|
||||||
use crate::repline::{error::Error, Repline};
|
|
||||||
|
|
||||||
let mut rl = Repline::new(self.mode.ansi_color(), self.prompt_begin, self.prompt_again);
|
|
||||||
fn clear_line() {
|
|
||||||
print!("\x1b[G\x1b[J");
|
|
||||||
}
|
|
||||||
loop {
|
|
||||||
let buf = match rl.read() {
|
|
||||||
Ok(buf) => buf,
|
|
||||||
// Ctrl-C: break if current line is empty
|
|
||||||
Err(Error::CtrlC(buf)) => {
|
|
||||||
if buf.is_empty() || buf.ends_with('\n') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
rl.accept();
|
|
||||||
println!("Cancelled. (Press Ctrl+C again to quit.)");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Ctrl-D: reset input, and parse it for errors
|
|
||||||
Err(Error::CtrlD(buf)) => {
|
|
||||||
rl.deny();
|
|
||||||
if let Err(e) = Program::new(&buf).parse() {
|
|
||||||
clear_line();
|
|
||||||
self.prompt_error(&e);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
self.prompt_error(&e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.begin_output();
|
|
||||||
if self.command(&buf) {
|
|
||||||
rl.deny();
|
|
||||||
rl.set_color(self.mode.ansi_color());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let code = Program::new(&buf);
|
|
||||||
if self.mode == Mode::Tokenize {
|
|
||||||
self.tokenize(&code);
|
|
||||||
rl.deny();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
match code.lex().into_iter().find(|l| l.is_err()) {
|
|
||||||
None => {}
|
|
||||||
Some(Ok(_)) => unreachable!(),
|
|
||||||
Some(Err(error)) => {
|
|
||||||
rl.deny();
|
|
||||||
self.prompt_error(&error);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Ok(mut code) = code.parse() {
|
|
||||||
rl.accept();
|
|
||||||
self.dispatch(&mut code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn help(&self) {
|
|
||||||
println!(
|
|
||||||
"Commands:\n- $tokens\n Tokenize Mode:\n Outputs information derived by the Lexer\n- $pretty\n Beautify Mode:\n Pretty-prints the input\n- $type\n Resolve Mode:\n Attempts variable resolution and type-checking on the input\n- $run\n Interpret Mode:\n Interprets the input using Conlang\'s work-in-progress interpreter\n- $mode\n Prints the current mode\n- $help\n Prints this help message"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
fn command(&mut self, line: &str) -> bool {
|
|
||||||
let Some(line) = line.trim().strip_prefix('$') else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
if let Ok(mode) = line.parse() {
|
|
||||||
self.mode = mode;
|
|
||||||
} else {
|
|
||||||
match line {
|
|
||||||
"$run" => self.mode = Mode::Interpret,
|
|
||||||
"mode" => println!("{:?} Mode", self.mode),
|
|
||||||
"help" => self.help(),
|
|
||||||
_ => return false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
/// Dispatches calls to repl functions based on the program
|
|
||||||
fn dispatch(&mut self, code: &mut Program<Parsed>) {
|
|
||||||
match self.mode {
|
|
||||||
Mode::Tokenize => {}
|
|
||||||
Mode::Beautify => self.beautify(code),
|
|
||||||
Mode::Interpret => self.interpret(code),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn tokenize(&mut self, code: &Program<Parsable>) {
|
|
||||||
for token in code.lex() {
|
|
||||||
match token {
|
|
||||||
Ok(token) => print_token(&token),
|
|
||||||
Err(e) => println!("{e}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn interpret(&mut self, code: &Program<Parsed>) {
|
|
||||||
match code.run(&mut self.env) {
|
|
||||||
Ok(ConValue::Empty) => {}
|
|
||||||
res => self.prompt_result(res),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn beautify(&mut self, code: &Program<Parsed>) {
|
|
||||||
code.print()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod tools {
|
|
||||||
use cl_token::Token;
|
|
||||||
use std::io::IsTerminal;
|
|
||||||
/// Prints a token in the particular way cl-repl does
|
|
||||||
pub fn print_token(t: &Token) {
|
|
||||||
println!(
|
|
||||||
"{:02}:{:02}: {:#19} │{}│",
|
|
||||||
t.line(),
|
|
||||||
t.col(),
|
|
||||||
t.ty(),
|
|
||||||
t.data(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/// gets whether stdin AND stdout are a terminal, for pipelining
|
|
||||||
pub fn is_terminal() -> bool {
|
|
||||||
std::io::stdin().is_terminal() && std::io::stdout().is_terminal()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod repline;
|
|
||||||
@@ -1,636 +0,0 @@
|
|||||||
//! A small pseudo-multiline editing library
|
|
||||||
// #![allow(unused)]
|
|
||||||
|
|
||||||
pub mod error {
|
|
||||||
/// Result type for Repline
|
|
||||||
pub type ReplResult<T> = std::result::Result<T, Error>;
|
|
||||||
/// Borrowed error (does not implement [Error](std::error::Error)!)
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
/// User broke with Ctrl+C
|
|
||||||
CtrlC(String),
|
|
||||||
/// User broke with Ctrl+D
|
|
||||||
CtrlD(String),
|
|
||||||
/// Invalid unicode codepoint
|
|
||||||
BadUnicode(u32),
|
|
||||||
/// Error came from [std::io]
|
|
||||||
IoFailure(std::io::Error),
|
|
||||||
/// End of input
|
|
||||||
EndOfInput,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for Error {}
|
|
||||||
impl std::fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Error::CtrlC(_) => write!(f, "Ctrl+C"),
|
|
||||||
Error::CtrlD(_) => write!(f, "Ctrl+D"),
|
|
||||||
Error::BadUnicode(u) => write!(f, "0x{u:x} is not a valid unicode codepoint"),
|
|
||||||
Error::IoFailure(s) => write!(f, "{s}"),
|
|
||||||
Error::EndOfInput => write!(f, "End of input"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<std::io::Error> for Error {
|
|
||||||
fn from(value: std::io::Error) -> Self {
|
|
||||||
Self::IoFailure(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod ignore {
|
|
||||||
//! Does nothing, universally.
|
|
||||||
//!
|
|
||||||
//! Introduces the [Ignore] trait, and its singular function, [ignore](Ignore::ignore),
|
|
||||||
//! which does nothing.
|
|
||||||
impl<T> Ignore for T {}
|
|
||||||
/// Does nothing
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
/// ```rust
|
|
||||||
/// #![deny(unused_must_use)]
|
|
||||||
/// # use cl_repl::repline::ignore::Ignore;
|
|
||||||
/// ().ignore();
|
|
||||||
/// Err::<(), &str>("Foo").ignore();
|
|
||||||
/// Some("Bar").ignore();
|
|
||||||
/// 42.ignore();
|
|
||||||
///
|
|
||||||
/// #[must_use]
|
|
||||||
/// fn the_meaning() -> usize {
|
|
||||||
/// 42
|
|
||||||
/// }
|
|
||||||
/// the_meaning().ignore();
|
|
||||||
/// ```
|
|
||||||
pub trait Ignore {
|
|
||||||
/// Does nothing
|
|
||||||
fn ignore(&self) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod chars {
|
|
||||||
//! Converts an <code>[Iterator]<Item = [u8]></code> into an
|
|
||||||
//! <code>[Iterator]<Item = [char]></code>
|
|
||||||
|
|
||||||
use super::error::*;
|
|
||||||
|
|
||||||
/// Converts an <code>[Iterator]<Item = [u8]></code> into an
|
|
||||||
/// <code>[Iterator]<Item = [char]></code>
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Chars<I: Iterator<Item = u8>>(pub I);
|
|
||||||
impl<I: Iterator<Item = u8>> Chars<I> {
|
|
||||||
pub fn new(bytes: I) -> Self {
|
|
||||||
Self(bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<I: Iterator<Item = u8>> Iterator for Chars<I> {
|
|
||||||
type Item = ReplResult<char>;
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
let Self(bytes) = self;
|
|
||||||
let start = bytes.next()? as u32;
|
|
||||||
let (mut out, count) = match start {
|
|
||||||
start if start & 0x80 == 0x00 => (start, 0), // ASCII valid range
|
|
||||||
start if start & 0xe0 == 0xc0 => (start & 0x1f, 1), // 1 continuation byte
|
|
||||||
start if start & 0xf0 == 0xe0 => (start & 0x0f, 2), // 2 continuation bytes
|
|
||||||
start if start & 0xf8 == 0xf0 => (start & 0x07, 3), // 3 continuation bytes
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
for _ in 0..count {
|
|
||||||
let cont = bytes.next()? as u32;
|
|
||||||
if cont & 0xc0 != 0x80 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
out = out << 6 | (cont & 0x3f);
|
|
||||||
}
|
|
||||||
Some(char::from_u32(out).ok_or(Error::BadUnicode(out)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod flatten {
|
|
||||||
//! Flattens an [Iterator] returning [`Result<T, E>`](Result) or [`Option<T>`](Option)
|
|
||||||
//! into a *non-[FusedIterator](std::iter::FusedIterator)* over `T`
|
|
||||||
|
|
||||||
/// Flattens an [Iterator] returning [`Result<T, E>`](Result) or [`Option<T>`](Option)
|
|
||||||
/// into a *non-[FusedIterator](std::iter::FusedIterator)* over `T`
|
|
||||||
pub struct Flatten<T, I: Iterator<Item = T>>(pub I);
|
|
||||||
impl<T, E, I: Iterator<Item = Result<T, E>>> Iterator for Flatten<Result<T, E>, I> {
|
|
||||||
type Item = T;
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
self.0.next()?.ok()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T, I: Iterator<Item = Option<T>>> Iterator for Flatten<Option<T>, I> {
|
|
||||||
type Item = T;
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
self.0.next()?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod raw {
|
|
||||||
//! Sets the terminal to [`raw`] mode for the duration of the returned object's lifetime.
|
|
||||||
|
|
||||||
/// Sets the terminal to raw mode for the duration of the returned object's lifetime.
|
|
||||||
pub fn raw() -> impl Drop {
|
|
||||||
Raw::default()
|
|
||||||
}
|
|
||||||
struct Raw();
|
|
||||||
impl Default for Raw {
|
|
||||||
fn default() -> Self {
|
|
||||||
std::thread::yield_now();
|
|
||||||
crossterm::terminal::enable_raw_mode()
|
|
||||||
.expect("should be able to transition into raw mode");
|
|
||||||
Raw()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Drop for Raw {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
crossterm::terminal::disable_raw_mode()
|
|
||||||
.expect("should be able to transition out of raw mode");
|
|
||||||
// std::thread::yield_now();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod out {
|
|
||||||
#![allow(unused)]
|
|
||||||
use std::io::{Result, Write};
|
|
||||||
|
|
||||||
/// A [Writer](Write) that flushes after every wipe
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub(super) struct EagerWriter<W: Write> {
|
|
||||||
out: W,
|
|
||||||
}
|
|
||||||
impl<W: Write> EagerWriter<W> {
|
|
||||||
pub fn new(writer: W) -> Self {
|
|
||||||
Self { out: writer }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<W: Write> Write for EagerWriter<W> {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
|
||||||
let out = self.out.write(buf)?;
|
|
||||||
self.out.flush()?;
|
|
||||||
Ok(out)
|
|
||||||
}
|
|
||||||
fn flush(&mut self) -> Result<()> {
|
|
||||||
self.out.flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use self::{chars::Chars, editor::Editor, error::*, flatten::Flatten, ignore::Ignore, raw::raw};
|
|
||||||
use std::{
|
|
||||||
collections::VecDeque,
|
|
||||||
io::{stdout, Bytes, Read, Result, Write},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Repline<'a, R: Read> {
|
|
||||||
input: Chars<Flatten<Result<u8>, Bytes<R>>>,
|
|
||||||
|
|
||||||
history: VecDeque<String>, // previous lines
|
|
||||||
hindex: usize, // current index into the history buffer
|
|
||||||
|
|
||||||
ed: Editor<'a>, // the current line buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, R: Read> Repline<'a, R> {
|
|
||||||
/// Constructs a [Repline] with the given [Reader](Read), color, begin, and again prompts.
|
|
||||||
pub fn with_input(input: R, color: &'a str, begin: &'a str, again: &'a str) -> Self {
|
|
||||||
Self {
|
|
||||||
input: Chars(Flatten(input.bytes())),
|
|
||||||
history: Default::default(),
|
|
||||||
hindex: 0,
|
|
||||||
ed: Editor::new(color, begin, again),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Set the terminal prompt color
|
|
||||||
pub fn set_color(&mut self, color: &'a str) {
|
|
||||||
self.ed.color = color
|
|
||||||
}
|
|
||||||
/// Reads in a line, and returns it for validation
|
|
||||||
pub fn read(&mut self) -> ReplResult<String> {
|
|
||||||
const INDENT: &str = " ";
|
|
||||||
let mut stdout = stdout().lock();
|
|
||||||
let stdout = &mut stdout;
|
|
||||||
let _make_raw = raw();
|
|
||||||
// self.ed.begin_frame(stdout)?;
|
|
||||||
// self.ed.redraw_frame(stdout)?;
|
|
||||||
self.ed.print_head(stdout)?;
|
|
||||||
loop {
|
|
||||||
stdout.flush()?;
|
|
||||||
match self.input.next().ok_or(Error::EndOfInput)?? {
|
|
||||||
// Ctrl+C: End of Text. Immediately exits.
|
|
||||||
// Ctrl+D: End of Transmission. Ends the current line.
|
|
||||||
'\x03' => {
|
|
||||||
drop(_make_raw);
|
|
||||||
writeln!(stdout)?;
|
|
||||||
return Err(Error::CtrlC(self.ed.to_string()));
|
|
||||||
}
|
|
||||||
'\x04' => {
|
|
||||||
drop(_make_raw);
|
|
||||||
writeln!(stdout)?;
|
|
||||||
return Err(Error::CtrlD(self.ed.to_string()));
|
|
||||||
}
|
|
||||||
// Tab: extend line by 4 spaces
|
|
||||||
'\t' => {
|
|
||||||
self.ed.extend(INDENT.chars(), stdout)?;
|
|
||||||
}
|
|
||||||
// ignore newlines, process line feeds. Not sure how cross-platform this is.
|
|
||||||
'\n' => {}
|
|
||||||
'\r' => {
|
|
||||||
self.ed.push('\n', stdout)?;
|
|
||||||
return Ok(self.ed.to_string());
|
|
||||||
}
|
|
||||||
// Escape sequence
|
|
||||||
'\x1b' => self.escape(stdout)?,
|
|
||||||
// backspace
|
|
||||||
'\x08' | '\x7f' => {
|
|
||||||
let ed = &mut self.ed;
|
|
||||||
if ed.ends_with(INDENT.chars()) {
|
|
||||||
for _ in 0..INDENT.len() {
|
|
||||||
ed.pop(stdout)?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ed.pop(stdout)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c if c.is_ascii_control() => {
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
eprint!("\\x{:02x}", c as u32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c => {
|
|
||||||
self.ed.push(c, stdout)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Handle ANSI Escape
|
|
||||||
fn escape<W: Write>(&mut self, w: &mut W) -> ReplResult<()> {
|
|
||||||
match self.input.next().ok_or(Error::EndOfInput)?? {
|
|
||||||
'[' => self.csi(w)?,
|
|
||||||
'O' => todo!("Process alternate character mode"),
|
|
||||||
other => self.ed.extend(['\x1b', other], w)?,
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
/// Handle ANSI Control Sequence Introducer
|
|
||||||
fn csi<W: Write>(&mut self, w: &mut W) -> ReplResult<()> {
|
|
||||||
match self.input.next().ok_or(Error::EndOfInput)?? {
|
|
||||||
'A' => {
|
|
||||||
self.hindex = self.hindex.saturating_sub(1);
|
|
||||||
self.restore_history(w)?
|
|
||||||
}
|
|
||||||
'B' => {
|
|
||||||
self.hindex = self
|
|
||||||
.hindex
|
|
||||||
.saturating_add(1)
|
|
||||||
.min(self.history.len().saturating_sub(1));
|
|
||||||
self.restore_history(w)?
|
|
||||||
}
|
|
||||||
'C' => self.ed.cursor_forward(1, w)?,
|
|
||||||
'D' => self.ed.cursor_back(1, w)?,
|
|
||||||
'H' => self.ed.home(w)?,
|
|
||||||
'F' => self.ed.end(w)?,
|
|
||||||
'3' => {
|
|
||||||
if let '~' = self.input.next().ok_or(Error::EndOfInput)?? {
|
|
||||||
self.ed.delete(w).ignore()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
other => {
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
eprint!("{}", other.escape_unicode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
/// Restores the currently selected history
|
|
||||||
pub fn restore_history<W: Write>(&mut self, w: &mut W) -> ReplResult<()> {
|
|
||||||
let Self { history, hindex, ed, .. } = self;
|
|
||||||
if !(0..history.len()).contains(hindex) {
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
ed.undraw(w)?;
|
|
||||||
ed.clear();
|
|
||||||
ed.print_head(w)?;
|
|
||||||
ed.extend(
|
|
||||||
history
|
|
||||||
.get(*hindex)
|
|
||||||
.expect("history should contain index")
|
|
||||||
.chars(),
|
|
||||||
w,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Append line to history and clear it
|
|
||||||
pub fn accept(&mut self) {
|
|
||||||
self.history_append(self.ed.iter().collect());
|
|
||||||
self.ed.clear();
|
|
||||||
self.hindex = self.history.len();
|
|
||||||
}
|
|
||||||
/// Append line to history
|
|
||||||
pub fn history_append(&mut self, mut buf: String) {
|
|
||||||
while buf.ends_with(char::is_whitespace) {
|
|
||||||
buf.pop();
|
|
||||||
}
|
|
||||||
if !self.history.contains(&buf) {
|
|
||||||
self.history.push_back(buf)
|
|
||||||
}
|
|
||||||
while self.history.len() > 20 {
|
|
||||||
self.history.pop_front();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Clear the line
|
|
||||||
pub fn deny(&mut self) {
|
|
||||||
self.ed.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Repline<'a, std::io::Stdin> {
|
|
||||||
pub fn new(color: &'a str, begin: &'a str, again: &'a str) -> Self {
|
|
||||||
Self::with_input(std::io::stdin(), color, begin, again)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod editor {
|
|
||||||
use crossterm::{cursor::*, execute, queue, style::*, terminal::*};
|
|
||||||
use std::{collections::VecDeque, fmt::Display, io::Write};
|
|
||||||
|
|
||||||
use super::error::{Error, ReplResult};
|
|
||||||
|
|
||||||
fn is_newline(c: &char) -> bool {
|
|
||||||
*c == '\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_chars<'a, W: Write>(
|
|
||||||
c: impl IntoIterator<Item = &'a char>,
|
|
||||||
w: &mut W,
|
|
||||||
) -> std::io::Result<()> {
|
|
||||||
for c in c {
|
|
||||||
write!(w, "{c}")?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Editor<'a> {
|
|
||||||
head: VecDeque<char>,
|
|
||||||
tail: VecDeque<char>,
|
|
||||||
|
|
||||||
pub color: &'a str,
|
|
||||||
begin: &'a str,
|
|
||||||
again: &'a str,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Editor<'a> {
|
|
||||||
pub fn new(color: &'a str, begin: &'a str, again: &'a str) -> Self {
|
|
||||||
Self { head: Default::default(), tail: Default::default(), color, begin, again }
|
|
||||||
}
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &char> {
|
|
||||||
self.head.iter()
|
|
||||||
}
|
|
||||||
pub fn undraw<W: Write>(&self, w: &mut W) -> ReplResult<()> {
|
|
||||||
let Self { head, .. } = self;
|
|
||||||
match head.iter().copied().filter(is_newline).count() {
|
|
||||||
0 => write!(w, "\x1b[0G"),
|
|
||||||
lines => write!(w, "\x1b[{}F", lines),
|
|
||||||
}?;
|
|
||||||
queue!(w, Clear(ClearType::FromCursorDown))?;
|
|
||||||
// write!(w, "\x1b[0J")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn redraw<W: Write>(&self, w: &mut W) -> ReplResult<()> {
|
|
||||||
let Self { head, tail, color, begin, again } = self;
|
|
||||||
write!(w, "{color}{begin}\x1b[0m ")?;
|
|
||||||
// draw head
|
|
||||||
for c in head {
|
|
||||||
match c {
|
|
||||||
'\n' => write!(w, "\r\n{color}{again}\x1b[0m "),
|
|
||||||
_ => w.write_all({ *c as u32 }.to_le_bytes().as_slice()),
|
|
||||||
}?
|
|
||||||
}
|
|
||||||
// save cursor
|
|
||||||
execute!(w, SavePosition)?;
|
|
||||||
// draw tail
|
|
||||||
for c in tail {
|
|
||||||
match c {
|
|
||||||
'\n' => write!(w, "\r\n{color}{again}\x1b[0m "),
|
|
||||||
_ => write!(w, "{c}"),
|
|
||||||
}?
|
|
||||||
}
|
|
||||||
// restore cursor
|
|
||||||
execute!(w, RestorePosition)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn prompt<W: Write>(&self, w: &mut W) -> ReplResult<()> {
|
|
||||||
let Self { head, color, begin, again, .. } = self;
|
|
||||||
queue!(
|
|
||||||
w,
|
|
||||||
MoveToColumn(0),
|
|
||||||
Print(color),
|
|
||||||
Print(if head.is_empty() { begin } else { again }),
|
|
||||||
ResetColor,
|
|
||||||
Print(' '),
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn print_head<W: Write>(&self, w: &mut W) -> ReplResult<()> {
|
|
||||||
self.prompt(w)?;
|
|
||||||
write_chars(
|
|
||||||
self.head.iter().skip(
|
|
||||||
self.head
|
|
||||||
.iter()
|
|
||||||
.rposition(is_newline)
|
|
||||||
.unwrap_or(self.head.len())
|
|
||||||
+ 1,
|
|
||||||
),
|
|
||||||
w,
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn print_tail<W: Write>(&self, w: &mut W) -> ReplResult<()> {
|
|
||||||
let Self { tail, .. } = self;
|
|
||||||
queue!(w, SavePosition, Clear(ClearType::UntilNewLine))?;
|
|
||||||
write_chars(tail.iter().take_while(|&c| !is_newline(c)), w)?;
|
|
||||||
queue!(w, RestorePosition)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn push<W: Write>(&mut self, c: char, w: &mut W) -> ReplResult<()> {
|
|
||||||
// Tail optimization: if the tail is empty,
|
|
||||||
//we don't have to undraw and redraw on newline
|
|
||||||
if self.tail.is_empty() {
|
|
||||||
self.head.push_back(c);
|
|
||||||
match c {
|
|
||||||
'\n' => {
|
|
||||||
write!(w, "\r\n")?;
|
|
||||||
self.print_head(w)?;
|
|
||||||
}
|
|
||||||
c => {
|
|
||||||
queue!(w, Print(c))?;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if '\n' == c {
|
|
||||||
self.undraw(w)?;
|
|
||||||
}
|
|
||||||
self.head.push_back(c);
|
|
||||||
match c {
|
|
||||||
'\n' => self.redraw(w)?,
|
|
||||||
_ => {
|
|
||||||
write!(w, "{c}")?;
|
|
||||||
self.print_tail(w)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn pop<W: Write>(&mut self, w: &mut W) -> ReplResult<Option<char>> {
|
|
||||||
if let Some('\n') = self.head.back() {
|
|
||||||
self.undraw(w)?;
|
|
||||||
}
|
|
||||||
let c = self.head.pop_back();
|
|
||||||
// if the character was a newline, we need to go back a line
|
|
||||||
match c {
|
|
||||||
Some('\n') => self.redraw(w)?,
|
|
||||||
Some(_) => {
|
|
||||||
// go back a char
|
|
||||||
queue!(w, MoveLeft(1), Print(' '), MoveLeft(1))?;
|
|
||||||
self.print_tail(w)?;
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn extend<T: IntoIterator<Item = char>, W: Write>(
|
|
||||||
&mut self,
|
|
||||||
iter: T,
|
|
||||||
w: &mut W,
|
|
||||||
) -> ReplResult<()> {
|
|
||||||
for c in iter {
|
|
||||||
self.push(c, w)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn restore(&mut self, s: &str) {
|
|
||||||
self.clear();
|
|
||||||
self.head.extend(s.chars())
|
|
||||||
}
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.head.clear();
|
|
||||||
self.tail.clear();
|
|
||||||
}
|
|
||||||
pub fn delete<W: Write>(&mut self, w: &mut W) -> ReplResult<char> {
|
|
||||||
match self.tail.front() {
|
|
||||||
Some('\n') => {
|
|
||||||
self.undraw(w)?;
|
|
||||||
let out = self.tail.pop_front();
|
|
||||||
self.redraw(w)?;
|
|
||||||
out
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let out = self.tail.pop_front();
|
|
||||||
self.print_tail(w)?;
|
|
||||||
out
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.ok_or(Error::EndOfInput)
|
|
||||||
}
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.head.len() + self.tail.len()
|
|
||||||
}
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.head.is_empty() && self.tail.is_empty()
|
|
||||||
}
|
|
||||||
pub fn ends_with(&self, iter: impl DoubleEndedIterator<Item = char>) -> bool {
|
|
||||||
let mut iter = iter.rev();
|
|
||||||
let mut head = self.head.iter().rev();
|
|
||||||
loop {
|
|
||||||
match (iter.next(), head.next()) {
|
|
||||||
(None, _) => break true,
|
|
||||||
(Some(_), None) => break false,
|
|
||||||
(Some(a), Some(b)) if a != *b => break false,
|
|
||||||
(Some(_), Some(_)) => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Moves the cursor back `steps` steps
|
|
||||||
pub fn cursor_back<W: Write>(&mut self, steps: usize, w: &mut W) -> ReplResult<()> {
|
|
||||||
for _ in 0..steps {
|
|
||||||
if let Some('\n') = self.head.back() {
|
|
||||||
self.undraw(w)?;
|
|
||||||
}
|
|
||||||
let Some(c) = self.head.pop_back() else {
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
self.tail.push_front(c);
|
|
||||||
match c {
|
|
||||||
'\n' => self.redraw(w)?,
|
|
||||||
_ => queue!(w, MoveLeft(1))?,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
/// Moves the cursor forward `steps` steps
|
|
||||||
pub fn cursor_forward<W: Write>(&mut self, steps: usize, w: &mut W) -> ReplResult<()> {
|
|
||||||
for _ in 0..steps {
|
|
||||||
if let Some('\n') = self.tail.front() {
|
|
||||||
self.undraw(w)?
|
|
||||||
}
|
|
||||||
let Some(c) = self.tail.pop_front() else {
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
self.head.push_back(c);
|
|
||||||
match c {
|
|
||||||
'\n' => self.redraw(w)?,
|
|
||||||
_ => queue!(w, MoveRight(1))?,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
/// Goes to the beginning of the current line
|
|
||||||
pub fn home<W: Write>(&mut self, w: &mut W) -> ReplResult<()> {
|
|
||||||
loop {
|
|
||||||
match self.head.back() {
|
|
||||||
Some('\n') | None => break Ok(()),
|
|
||||||
Some(_) => self.cursor_back(1, w)?,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Goes to the end of the current line
|
|
||||||
pub fn end<W: Write>(&mut self, w: &mut W) -> ReplResult<()> {
|
|
||||||
loop {
|
|
||||||
match self.tail.front() {
|
|
||||||
Some('\n') | None => break Ok(()),
|
|
||||||
Some(_) => self.cursor_forward(1, w)?,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'e> IntoIterator for &'e Editor<'a> {
|
|
||||||
type Item = &'e char;
|
|
||||||
type IntoIter = std::iter::Chain<
|
|
||||||
std::collections::vec_deque::Iter<'e, char>,
|
|
||||||
std::collections::vec_deque::Iter<'e, char>,
|
|
||||||
>;
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.head.iter().chain(self.tail.iter())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a> Display for Editor<'a> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
use std::fmt::Write;
|
|
||||||
let Self { head, tail, .. } = self;
|
|
||||||
for c in head {
|
|
||||||
f.write_char(*c)?;
|
|
||||||
}
|
|
||||||
for c in tail {
|
|
||||||
f.write_char(*c)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
//! Trivially-copyable, easily comparable typed indices, and a [Pool] to contain them
|
|
||||||
//!
|
|
||||||
//! # Examples
|
|
||||||
//!
|
|
||||||
//! ```rust
|
|
||||||
//! # use cl_structures::intern_pool::*;
|
|
||||||
//! // first, create a new InternKey type (this ensures type safety)
|
|
||||||
//! make_intern_key!{
|
|
||||||
//! NumbersKey
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! // then, create a pool with that type
|
|
||||||
//! let mut numbers: Pool<i32, NumbersKey> = Pool::new();
|
|
||||||
//! let first = numbers.insert(1);
|
|
||||||
//! let second = numbers.insert(2);
|
|
||||||
//! let third = numbers.insert(3);
|
|
||||||
//!
|
|
||||||
//! // You can access elements immutably with `get`
|
|
||||||
//! assert_eq!(Some(&3), numbers.get(third));
|
|
||||||
//! assert_eq!(Some(&2), numbers.get(second));
|
|
||||||
//! // or by indexing
|
|
||||||
//! assert_eq!(1, numbers[first]);
|
|
||||||
//!
|
|
||||||
//! // Or mutably
|
|
||||||
//! *numbers.get_mut(first).unwrap() = 100000;
|
|
||||||
//!
|
|
||||||
//! assert_eq!(Some(&100000), numbers.get(first));
|
|
||||||
//! ```
|
|
||||||
|
|
||||||
/// Creates newtype indices over [`usize`] for use as [Pool] keys.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! make_intern_key {($($(#[$meta:meta])* $name:ident),*$(,)?) => {$(
|
|
||||||
$(#[$meta])*
|
|
||||||
#[repr(transparent)]
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
pub struct $name(usize);
|
|
||||||
|
|
||||||
impl $crate::intern_pool::InternKey for $name {
|
|
||||||
#[doc = concat!("Constructs a [`", stringify!($name), "`] from a [`usize`] without checking bounds.\n")]
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The provided value should be within the bounds of its associated container
|
|
||||||
unsafe fn from_raw_unchecked(value: usize) -> Self {
|
|
||||||
Self(value)
|
|
||||||
}
|
|
||||||
fn get(&self) -> usize {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From< $name > for usize {
|
|
||||||
fn from(value: $name) -> Self {
|
|
||||||
value.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*}}
|
|
||||||
use std::ops::{Index, IndexMut};
|
|
||||||
|
|
||||||
pub use make_intern_key;
|
|
||||||
|
|
||||||
/// An index into a [Pool]. For full type-safety,
|
|
||||||
/// there should be a unique [InternKey] for each [Pool]
|
|
||||||
pub trait InternKey: std::fmt::Debug {
|
|
||||||
/// Constructs an [`InternKey`] from a [`usize`] without checking bounds.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The provided value should be within the bounds of its associated container.
|
|
||||||
// ID::from_raw_unchecked here isn't *actually* unsafe, since bounds should always be
|
|
||||||
// checked, however, the function has unverifiable preconditions.
|
|
||||||
unsafe fn from_raw_unchecked(value: usize) -> Self;
|
|
||||||
/// Gets the index of the [`InternKey`] by value
|
|
||||||
fn get(&self) -> usize;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Pool<T, ID: InternKey> {
|
|
||||||
pool: Vec<T>,
|
|
||||||
id_type: std::marker::PhantomData<ID>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, ID: InternKey> Pool<T, ID> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
pub fn get(&self, index: ID) -> Option<&T> {
|
|
||||||
self.pool.get(index.get())
|
|
||||||
}
|
|
||||||
pub fn get_mut(&mut self, index: ID) -> Option<&mut T> {
|
|
||||||
self.pool.get_mut(index.get())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
|
||||||
self.pool.iter()
|
|
||||||
}
|
|
||||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
|
|
||||||
self.pool.iter_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert(&mut self, value: T) -> ID {
|
|
||||||
let id = self.pool.len();
|
|
||||||
self.pool.push(value);
|
|
||||||
|
|
||||||
// Safety: value was pushed to `self.pool[id]`
|
|
||||||
unsafe { ID::from_raw_unchecked(id) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, ID: InternKey> Default for Pool<T, ID> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self { pool: vec![], id_type: std::marker::PhantomData }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, ID: InternKey> Index<ID> for Pool<T, ID> {
|
|
||||||
type Output = T;
|
|
||||||
|
|
||||||
fn index(&self, index: ID) -> &Self::Output {
|
|
||||||
match self.pool.get(index.get()) {
|
|
||||||
None => panic!("Index {:?} out of bounds in pool!", index),
|
|
||||||
Some(value) => value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T, ID: InternKey> IndexMut<ID> for Pool<T, ID> {
|
|
||||||
fn index_mut(&mut self, index: ID) -> &mut Self::Output {
|
|
||||||
match self.pool.get_mut(index.get()) {
|
|
||||||
None => panic!("Index {:?} out of bounds in pool!", index),
|
|
||||||
Some(value) => value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
//! # Universally useful structures
|
|
||||||
//! - [Span](struct@span::Span): Stores a start and end [Loc](struct@span::Loc)
|
|
||||||
//! - [Loc](struct@span::Loc): Stores the index in a stream
|
|
||||||
#![warn(clippy::all)]
|
|
||||||
#![feature(inline_const, dropck_eyepatch, decl_macro)]
|
|
||||||
#![deny(unsafe_op_in_unsafe_fn)]
|
|
||||||
|
|
||||||
pub mod span;
|
|
||||||
|
|
||||||
pub mod tree;
|
|
||||||
|
|
||||||
pub mod stack;
|
|
||||||
|
|
||||||
pub mod intern_pool;
|
|
||||||
@@ -1,909 +0,0 @@
|
|||||||
//! # The Conlang Type Checker
|
|
||||||
//!
|
|
||||||
//! As a statically typed language, Conlang requires a robust type checker to enforce correctness.
|
|
||||||
#![feature(debug_closure_helpers)]
|
|
||||||
#![warn(clippy::all)]
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
The type checker keeps track of a *global intern pool* for Types and Values
|
|
||||||
References to the intern pool are held by ID, and items cannot be freed from the pool EVER.
|
|
||||||
|
|
||||||
Items are inserted into their respective pools,
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub mod key {
|
|
||||||
use cl_structures::intern_pool::*;
|
|
||||||
|
|
||||||
// define the index types
|
|
||||||
make_intern_key! {
|
|
||||||
/// Uniquely represents a [Def][1] in the [Def][1] [Pool]
|
|
||||||
///
|
|
||||||
/// [1]: crate::definition::Def
|
|
||||||
DefID,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod definition {
|
|
||||||
use crate::{key::DefID, module::Module, type_kind::TypeKind, value_kind::ValueKind};
|
|
||||||
use cl_ast::{format::FmtPretty, Item, Meta, Visibility};
|
|
||||||
use std::fmt::Write;
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
|
||||||
pub struct Def {
|
|
||||||
pub name: String,
|
|
||||||
pub vis: Visibility,
|
|
||||||
pub meta: Vec<Meta>,
|
|
||||||
pub kind: DefKind,
|
|
||||||
pub source: Option<Item>,
|
|
||||||
pub module: Module,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Def {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
name: Default::default(),
|
|
||||||
vis: Default::default(),
|
|
||||||
meta: Default::default(),
|
|
||||||
kind: DefKind::Type(TypeKind::Module),
|
|
||||||
source: Default::default(),
|
|
||||||
module: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Def {
|
|
||||||
pub fn new_module(
|
|
||||||
name: String,
|
|
||||||
vis: Visibility,
|
|
||||||
meta: Vec<Meta>,
|
|
||||||
parent: Option<DefID>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
name,
|
|
||||||
vis,
|
|
||||||
meta,
|
|
||||||
kind: DefKind::Type(TypeKind::Module),
|
|
||||||
source: None,
|
|
||||||
module: Module { parent, ..Default::default() },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for Def {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { name, vis, meta, kind, source, module } = self;
|
|
||||||
f.debug_struct("Def")
|
|
||||||
.field("name", &name)
|
|
||||||
.field("vis", &vis)
|
|
||||||
.field_with("meta", |f| write!(f, "{meta:?}"))
|
|
||||||
.field("kind", &kind)
|
|
||||||
.field_with("source", |f| match source {
|
|
||||||
Some(item) => write!(f.pretty(), "{{\n{item}\n}}"),
|
|
||||||
None => todo!(),
|
|
||||||
})
|
|
||||||
.field("module", &module)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum DefKind {
|
|
||||||
/// A type, such as a ``
|
|
||||||
Type(TypeKind),
|
|
||||||
/// A value, such as a `const`, `static`, or `fn`
|
|
||||||
Value(ValueKind),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DefKind {
|
|
||||||
pub fn is_type(&self) -> bool {
|
|
||||||
matches!(self, Self::Type(_))
|
|
||||||
}
|
|
||||||
pub fn ty(&self) -> Option<&TypeKind> {
|
|
||||||
match self {
|
|
||||||
DefKind::Type(t) => Some(t),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn is_value(&self) -> bool {
|
|
||||||
matches!(self, Self::Value(_))
|
|
||||||
}
|
|
||||||
pub fn value(&self) -> Option<&ValueKind> {
|
|
||||||
match self {
|
|
||||||
DefKind::Value(v) => Some(v),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod type_kind {
|
|
||||||
//! A [TypeKind] represents an item in the Type Namespace
|
|
||||||
//! (a component of a [Project](crate::project::Project)).
|
|
||||||
|
|
||||||
use cl_ast::Visibility;
|
|
||||||
use std::{fmt::Debug, str::FromStr};
|
|
||||||
|
|
||||||
use crate::key::DefID;
|
|
||||||
|
|
||||||
/// The kind of a type
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum TypeKind {
|
|
||||||
/// A type which has not yet been resolved
|
|
||||||
Undecided,
|
|
||||||
/// An alias for an already-defined type
|
|
||||||
Alias(Option<DefID>),
|
|
||||||
/// A primitive type, built-in to the compiler
|
|
||||||
Intrinsic(Intrinsic),
|
|
||||||
/// A user-defined abstract data type
|
|
||||||
Adt(Adt),
|
|
||||||
/// A reference to an already-defined type: &T
|
|
||||||
Ref(DefID),
|
|
||||||
/// A contiguous view of dynamically sized memory
|
|
||||||
Slice(DefID),
|
|
||||||
/// A function pointer which accepts multiple inputs and produces an output
|
|
||||||
FnPtr { args: Vec<DefID>, rety: DefID },
|
|
||||||
/// The unit type
|
|
||||||
Empty,
|
|
||||||
/// The never type
|
|
||||||
Never,
|
|
||||||
/// The Self type
|
|
||||||
SelfTy,
|
|
||||||
/// An untyped module
|
|
||||||
Module,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A user-defined Abstract Data Type
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum Adt {
|
|
||||||
/// A union-like enum type
|
|
||||||
Enum(Vec<(String, DefID)>),
|
|
||||||
CLikeEnum(Vec<(String, u128)>),
|
|
||||||
/// An enum with no fields, which can never be constructed
|
|
||||||
FieldlessEnum,
|
|
||||||
|
|
||||||
/// A structural product type with named members
|
|
||||||
Struct(Vec<(String, Visibility, DefID)>),
|
|
||||||
/// A structural product type with unnamed members
|
|
||||||
TupleStruct(Vec<(Visibility, DefID)>),
|
|
||||||
/// A structural product type of neither named nor unnamed members
|
|
||||||
UnitStruct,
|
|
||||||
|
|
||||||
/// A choose your own undefined behavior type
|
|
||||||
/// TODO: should unions be a language feature?
|
|
||||||
Union(Vec<(String, DefID)>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The set of compiler-intrinsic types.
|
|
||||||
/// These primitive types have native implementations of the basic operations.
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum Intrinsic {
|
|
||||||
/// An 8-bit signed integer: `#[intrinsic = "i8"]`
|
|
||||||
I8,
|
|
||||||
/// A 16-bit signed integer: `#[intrinsic = "i16"]`
|
|
||||||
I16,
|
|
||||||
/// A 32-bit signed integer: `#[intrinsic = "i32"]`
|
|
||||||
I32,
|
|
||||||
/// A 64-bit signed integer: `#[intrinsic = "i32"]`
|
|
||||||
I64,
|
|
||||||
// /// A 128-bit signed integer: `#[intrinsic = "i32"]`
|
|
||||||
// I128,
|
|
||||||
/// An 8-bit unsigned integer: `#[intrinsic = "u8"]`
|
|
||||||
U8,
|
|
||||||
/// A 16-bit unsigned integer: `#[intrinsic = "u16"]`
|
|
||||||
U16,
|
|
||||||
/// A 32-bit unsigned integer: `#[intrinsic = "u32"]`
|
|
||||||
U32,
|
|
||||||
/// A 64-bit unsigned integer: `#[intrinsic = "u64"]`
|
|
||||||
U64,
|
|
||||||
// /// A 128-bit unsigned integer: `#[intrinsic = "u128"]`
|
|
||||||
// U128,
|
|
||||||
/// A boolean (`true` or `false`): `#[intrinsic = "bool"]`
|
|
||||||
Bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Intrinsic {
|
|
||||||
type Err = ();
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
Ok(match s {
|
|
||||||
"i8" => Intrinsic::I8,
|
|
||||||
"i16" => Intrinsic::I16,
|
|
||||||
"i32" => Intrinsic::I32,
|
|
||||||
"i64" => Intrinsic::I64,
|
|
||||||
"u8" => Intrinsic::U8,
|
|
||||||
"u16" => Intrinsic::U16,
|
|
||||||
"u32" => Intrinsic::U32,
|
|
||||||
"u64" => Intrinsic::U64,
|
|
||||||
"bool" => Intrinsic::Bool,
|
|
||||||
_ => Err(())?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum Float {
|
|
||||||
F32 = 0x20,
|
|
||||||
F64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod value_kind {
|
|
||||||
//! A [ValueKind] represents an item in the Value Namespace
|
|
||||||
//! (a component of a [Project](crate::project::Project)).
|
|
||||||
|
|
||||||
use crate::typeref::TypeRef;
|
|
||||||
use cl_ast::Block;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum ValueKind {
|
|
||||||
Undecided,
|
|
||||||
Const(TypeRef),
|
|
||||||
Static(TypeRef),
|
|
||||||
Fn {
|
|
||||||
// TODO: Store the variable bindings here!
|
|
||||||
args: Vec<TypeRef>,
|
|
||||||
rety: TypeRef,
|
|
||||||
body: Block,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod module {
|
|
||||||
//! A [Module] is a node in the Module Tree (a component of a
|
|
||||||
//! [Project](crate::project::Project))
|
|
||||||
use crate::key::DefID;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
/// A [Module] is a node in the Module Tree (a component of a
|
|
||||||
/// [Project](crate::project::Project)).
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
|
||||||
pub struct Module {
|
|
||||||
pub parent: Option<DefID>,
|
|
||||||
pub types: HashMap<String, DefID>,
|
|
||||||
pub values: HashMap<String, DefID>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Module {
|
|
||||||
pub fn new(parent: DefID) -> Self {
|
|
||||||
Self { parent: Some(parent), ..Default::default() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod path {
|
|
||||||
use cl_ast::{Path as AstPath, PathPart};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Path<'p> {
|
|
||||||
pub absolute: bool,
|
|
||||||
pub parts: &'p [PathPart],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'p> Path<'p> {
|
|
||||||
pub fn new(path: &'p AstPath) -> Self {
|
|
||||||
let AstPath { absolute, parts } = path;
|
|
||||||
Self { absolute: *absolute, parts }
|
|
||||||
}
|
|
||||||
pub fn relative(self) -> Self {
|
|
||||||
Self { absolute: false, ..self }
|
|
||||||
}
|
|
||||||
pub fn pop_front(self) -> Option<Self> {
|
|
||||||
let Self { absolute, parts } = self;
|
|
||||||
Some(Self { absolute, parts: parts.get(1..)? })
|
|
||||||
}
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.parts.is_empty()
|
|
||||||
}
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.parts.len()
|
|
||||||
}
|
|
||||||
pub fn front(&self) -> Option<&PathPart> {
|
|
||||||
self.parts.first()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'p> From<&'p AstPath> for Path<'p> {
|
|
||||||
fn from(value: &'p AstPath) -> Self {
|
|
||||||
Self::new(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl std::fmt::Display for Path<'_> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
const SEPARATOR: &str = "::";
|
|
||||||
let Self { absolute, parts } = self;
|
|
||||||
if *absolute {
|
|
||||||
write!(f, "{SEPARATOR}")?
|
|
||||||
}
|
|
||||||
for (idx, part) in parts.iter().enumerate() {
|
|
||||||
write!(f, "{}{part}", if idx > 0 { SEPARATOR } else { "" })?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod project {
|
|
||||||
use crate::{
|
|
||||||
definition::{Def, DefKind},
|
|
||||||
key::DefID,
|
|
||||||
path::Path,
|
|
||||||
type_kind::TypeKind,
|
|
||||||
};
|
|
||||||
use cl_ast::{Identifier, PathPart, Visibility};
|
|
||||||
use cl_structures::intern_pool::Pool;
|
|
||||||
use std::ops::{Index, IndexMut};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Project {
|
|
||||||
pub pool: Pool<Def, DefID>,
|
|
||||||
pub module_root: DefID,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Project {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Default for Project {
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut pool = Pool::default();
|
|
||||||
let module_root = pool.insert(Def::default());
|
|
||||||
// Insert the Never(!) type
|
|
||||||
let never = pool.insert(Def {
|
|
||||||
name: String::from("!"),
|
|
||||||
vis: Visibility::Public,
|
|
||||||
kind: DefKind::Type(TypeKind::Never),
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
pool[module_root]
|
|
||||||
.module
|
|
||||||
.types
|
|
||||||
.insert(String::from("!"), never);
|
|
||||||
Self { pool, module_root }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Project {
|
|
||||||
pub fn parent_of(&self, module: DefID) -> Option<DefID> {
|
|
||||||
self[module].module.parent
|
|
||||||
}
|
|
||||||
pub fn root_of(&self, module: DefID) -> DefID {
|
|
||||||
match self.parent_of(module) {
|
|
||||||
Some(module) => self.root_of(module),
|
|
||||||
None => module,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Resolves a path within a module tree, finding the innermost module.
|
|
||||||
/// Returns the remaining path parts.
|
|
||||||
pub fn get_type<'a>(&self, path: Path<'a>, within: DefID) -> Option<(DefID, Path<'a>)> {
|
|
||||||
// TODO: Cache module lookups
|
|
||||||
if path.absolute {
|
|
||||||
self.get_type(path.relative(), self.root_of(within))
|
|
||||||
} else if let Some(front) = path.front() {
|
|
||||||
let module = &self[within].module;
|
|
||||||
match front {
|
|
||||||
PathPart::SelfKw => self.get_type(path.pop_front()?, within),
|
|
||||||
PathPart::SuperKw => self.get_type(path.pop_front()?, module.parent?),
|
|
||||||
PathPart::Ident(Identifier(name)) => match module.types.get(name) {
|
|
||||||
Some(&submodule) => self.get_type(path.pop_front()?, submodule),
|
|
||||||
None => Some((within, path)),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Some((within, path))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_value<'a>(&self, path: Path<'a>, within: DefID) -> Option<(DefID, Path<'a>)> {
|
|
||||||
match path.front()? {
|
|
||||||
PathPart::Ident(Identifier(name)) => Some((
|
|
||||||
self[within].module.values.get(name).copied()?,
|
|
||||||
path.pop_front()?,
|
|
||||||
)),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rustfmt::skip]
|
|
||||||
pub fn insert_type(&mut self, name: String, value: Def, parent: DefID) -> Option<DefID> {
|
|
||||||
let id = self.pool.insert(value);
|
|
||||||
self[parent].module.types.insert(name, id)
|
|
||||||
}
|
|
||||||
#[rustfmt::skip]
|
|
||||||
pub fn insert_value(&mut self, name: String, value: Def, parent: DefID) -> Option<DefID> {
|
|
||||||
let id = self.pool.insert(value);
|
|
||||||
self[parent].module.values.insert(name, id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implements [Index] and [IndexMut] for [Project]: `self.table[ID] -> Definition`
|
|
||||||
macro_rules! impl_index {
|
|
||||||
($(self.$table:ident[$idx:ty] -> $out:ty),*$(,)?) => {$(
|
|
||||||
impl Index<$idx> for Project {
|
|
||||||
type Output = $out;
|
|
||||||
fn index(&self, index: $idx) -> &Self::Output {
|
|
||||||
&self.$table[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl IndexMut<$idx> for Project {
|
|
||||||
fn index_mut(&mut self, index: $idx) -> &mut Self::Output {
|
|
||||||
&mut self.$table[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*};
|
|
||||||
}
|
|
||||||
impl_index! {
|
|
||||||
self.pool[DefID] -> Def,
|
|
||||||
// self.types[TypeID] -> TypeDef,
|
|
||||||
// self.values[ValueID] -> ValueDef,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod name_collector {
|
|
||||||
//! Performs step 1 of type checking: Collecting all the names of things into [Module] units
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
definition::{Def, DefKind},
|
|
||||||
key,
|
|
||||||
project::Project,
|
|
||||||
type_kind::{Adt, TypeKind},
|
|
||||||
value_kind::ValueKind,
|
|
||||||
};
|
|
||||||
use cl_ast::*;
|
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
/// Collects types for future use
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub struct NameCollector<'prj> {
|
|
||||||
/// A stack of the current modules
|
|
||||||
pub mod_stack: Vec<key::DefID>,
|
|
||||||
/// The [Project], the type checker and resolver's central data store
|
|
||||||
pub project: &'prj mut Project,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'prj> NameCollector<'prj> {
|
|
||||||
pub fn new(project: &'prj mut Project) -> Self {
|
|
||||||
// create a root module
|
|
||||||
Self { mod_stack: vec![project.module_root], project }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the currently traversed parent module
|
|
||||||
pub fn parent(&self) -> Option<key::DefID> {
|
|
||||||
self.mod_stack.last().copied()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for NameCollector<'_> {
|
|
||||||
type Target = Project;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
self.project
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl DerefMut for NameCollector<'_> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
self.project
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NameCollector<'_> {
|
|
||||||
pub fn file(&mut self, f: &File) -> Result<(), &'static str> {
|
|
||||||
let parent = self.parent().ok_or("No parent to add item to")?;
|
|
||||||
|
|
||||||
for item in &f.items {
|
|
||||||
let def = match &item.kind {
|
|
||||||
// modules
|
|
||||||
// types
|
|
||||||
ItemKind::Module(_) => {
|
|
||||||
self.module(item)?;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ItemKind::Enum(_) => Some(self.ty_enum(item)?),
|
|
||||||
ItemKind::Alias(_) => Some(self.ty_alias(item)?),
|
|
||||||
ItemKind::Struct(_) => Some(self.ty_struct(item)?),
|
|
||||||
// values processed by the value collector
|
|
||||||
ItemKind::Const(_) => Some(self.val_const(item)?),
|
|
||||||
ItemKind::Static(_) => Some(self.val_static(item)?),
|
|
||||||
ItemKind::Function(_) => Some(self.val_function(item)?),
|
|
||||||
ItemKind::Impl(_) => None,
|
|
||||||
};
|
|
||||||
let Some(def) = def else { continue };
|
|
||||||
match def.kind {
|
|
||||||
DefKind::Type(_) => {
|
|
||||||
if let Some(v) = self.insert_type(def.name.clone(), def, parent) {
|
|
||||||
panic!("Redefinition of type {} ({v:?})!", self[v].name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DefKind::Value(_) => {
|
|
||||||
if let Some(v) = self.insert_value(def.name.clone(), def, parent) {
|
|
||||||
panic!("Redefinition of value {} ({v:?})!", self[v].name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Collects a [Module]
|
|
||||||
pub fn module(&mut self, m: &Item) -> Result<(), &'static str> {
|
|
||||||
let Item { kind: ItemKind::Module(Module { name, kind }), vis, attrs, .. } = m else {
|
|
||||||
Err("module called on Item which was not an ItemKind::Module")?
|
|
||||||
};
|
|
||||||
let ModuleKind::Inline(kind) = kind else {
|
|
||||||
Err("Out-of-line modules not yet supported")?
|
|
||||||
};
|
|
||||||
let parent = self.parent().ok_or("No parent to add module to")?;
|
|
||||||
|
|
||||||
let module = self.pool.insert(Def::new_module(
|
|
||||||
name.0.clone(),
|
|
||||||
*vis,
|
|
||||||
attrs.meta.clone(),
|
|
||||||
Some(parent),
|
|
||||||
));
|
|
||||||
|
|
||||||
self[parent]
|
|
||||||
.module
|
|
||||||
.types
|
|
||||||
.insert(name.0.clone(), module)
|
|
||||||
.is_some()
|
|
||||||
.then(|| panic!("Error: redefinition of module {name}"));
|
|
||||||
|
|
||||||
self.mod_stack.push(module);
|
|
||||||
let out = self.file(kind);
|
|
||||||
self.mod_stack.pop();
|
|
||||||
|
|
||||||
out
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Type collection
|
|
||||||
impl NameCollector<'_> {
|
|
||||||
/// Collects an [Item] of type [ItemKind::Enum]
|
|
||||||
pub fn ty_enum(&mut self, item: &Item) -> Result<Def, &'static str> {
|
|
||||||
let Item { kind: ItemKind::Enum(Enum { name, kind }), vis, attrs, .. } = item else {
|
|
||||||
Err("Enum called on item which was not ItemKind::Enum")?
|
|
||||||
};
|
|
||||||
let kind = match kind {
|
|
||||||
EnumKind::NoVariants => DefKind::Type(TypeKind::Adt(Adt::FieldlessEnum)),
|
|
||||||
EnumKind::Variants(_) => DefKind::Type(TypeKind::Undecided),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Def {
|
|
||||||
name: name.0.clone(),
|
|
||||||
vis: *vis,
|
|
||||||
meta: attrs.meta.clone(),
|
|
||||||
kind,
|
|
||||||
source: Some(item.clone()),
|
|
||||||
module: Default::default(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Collects an [Item] of type [ItemKind::Alias]
|
|
||||||
pub fn ty_alias(&mut self, item: &Item) -> Result<Def, &'static str> {
|
|
||||||
let Item { kind: ItemKind::Alias(Alias { to: name, from }), vis, attrs, .. } = item
|
|
||||||
else {
|
|
||||||
Err("Alias called on Item which was not ItemKind::Alias")?
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut kind = match from {
|
|
||||||
Some(_) => DefKind::Type(TypeKind::Undecided),
|
|
||||||
None => DefKind::Type(TypeKind::Alias(None)),
|
|
||||||
};
|
|
||||||
|
|
||||||
for meta in &attrs.meta {
|
|
||||||
let Meta { name: meta_name, kind: meta_kind } = meta;
|
|
||||||
match (meta_name.0.as_str(), meta_kind) {
|
|
||||||
("intrinsic", MetaKind::Equals(Literal::String(intrinsic))) => {
|
|
||||||
kind = DefKind::Type(TypeKind::Intrinsic(
|
|
||||||
intrinsic.parse().map_err(|_| "unknown intrinsic type")?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
("intrinsic", MetaKind::Plain) => {
|
|
||||||
kind = DefKind::Type(TypeKind::Intrinsic(
|
|
||||||
name.0.parse().map_err(|_| "Unknown intrinsic type")?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Def {
|
|
||||||
name: name.0.clone(),
|
|
||||||
vis: *vis,
|
|
||||||
meta: attrs.meta.clone(),
|
|
||||||
kind,
|
|
||||||
source: Some(item.clone()),
|
|
||||||
module: Default::default(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Collects an [Item] of type [ItemKind::Struct]
|
|
||||||
pub fn ty_struct(&mut self, item: &Item) -> Result<Def, &'static str> {
|
|
||||||
let Item { kind: ItemKind::Struct(Struct { name, kind }), vis, attrs, .. } = item
|
|
||||||
else {
|
|
||||||
Err("Struct called on item which was not ItemKind::Struct")?
|
|
||||||
};
|
|
||||||
let kind = match kind {
|
|
||||||
StructKind::Empty => DefKind::Type(TypeKind::Adt(Adt::UnitStruct)),
|
|
||||||
StructKind::Tuple(_) => DefKind::Type(TypeKind::Undecided),
|
|
||||||
StructKind::Struct(_) => DefKind::Type(TypeKind::Undecided),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Def {
|
|
||||||
name: name.0.clone(),
|
|
||||||
vis: *vis,
|
|
||||||
meta: attrs.meta.clone(),
|
|
||||||
kind,
|
|
||||||
source: Some(item.clone()),
|
|
||||||
module: Default::default(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Value collection
|
|
||||||
impl NameCollector<'_> {
|
|
||||||
pub fn val_const(&mut self, item: &Item) -> Result<Def, &'static str> {
|
|
||||||
let Item { kind: ItemKind::Const(Const { name, .. }), vis, attrs, .. } = item else {
|
|
||||||
Err("Const called on Item which was not ItemKind::Const")?
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Def {
|
|
||||||
name: name.0.clone(),
|
|
||||||
vis: *vis,
|
|
||||||
meta: attrs.meta.clone(),
|
|
||||||
kind: DefKind::Value(ValueKind::Undecided),
|
|
||||||
source: Some(item.clone()),
|
|
||||||
module: Default::default(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn val_static(&mut self, item: &Item) -> Result<Def, &'static str> {
|
|
||||||
let Item { kind: ItemKind::Static(Static { name, .. }), vis, attrs, .. } = item else {
|
|
||||||
Err("Static called on Item which was not ItemKind::Static")?
|
|
||||||
};
|
|
||||||
Ok(Def {
|
|
||||||
name: name.0.clone(),
|
|
||||||
vis: *vis,
|
|
||||||
meta: attrs.meta.clone(),
|
|
||||||
kind: DefKind::Type(TypeKind::Undecided),
|
|
||||||
source: Some(item.clone()),
|
|
||||||
module: Default::default(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn val_function(&mut self, item: &Item) -> Result<Def, &'static str> {
|
|
||||||
// TODO: treat function bodies like modules with internal items
|
|
||||||
let Item { kind: ItemKind::Function(Function { name, .. }), vis, attrs, .. } = item
|
|
||||||
else {
|
|
||||||
Err("val_function called on Item which was not ItemKind::Function")?
|
|
||||||
};
|
|
||||||
Ok(Def {
|
|
||||||
name: name.0.clone(),
|
|
||||||
vis: *vis,
|
|
||||||
meta: attrs.meta.clone(),
|
|
||||||
kind: DefKind::Value(ValueKind::Undecided),
|
|
||||||
source: Some(item.clone()),
|
|
||||||
module: Default::default(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod type_resolver {
|
|
||||||
//! Performs step 2 of type checking: Evaluating type definitions
|
|
||||||
#![allow(unused)]
|
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
use cl_ast::*;
|
|
||||||
|
|
||||||
use crate::{definition::Def, key::DefID, project::Project};
|
|
||||||
|
|
||||||
pub struct TypeResolver<'prj> {
|
|
||||||
pub project: &'prj mut Project,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for TypeResolver<'_> {
|
|
||||||
type Target = Project;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
self.project
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl DerefMut for TypeResolver<'_> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
self.project
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TypeResolver<'_> {
|
|
||||||
pub fn resolve(&mut self) -> Result<bool, &str> {
|
|
||||||
#![allow(unused)]
|
|
||||||
for typedef in self.pool.iter_mut().filter(|v| v.kind.is_type()) {
|
|
||||||
let Def { name, vis, meta: attr, kind, source: Some(ref definition), module: _ } =
|
|
||||||
typedef
|
|
||||||
else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
match &definition.kind {
|
|
||||||
ItemKind::Alias(Alias { to: _, from: Some(from) }) => match &from.kind {
|
|
||||||
TyKind::Never => todo!(),
|
|
||||||
TyKind::Empty => todo!(),
|
|
||||||
TyKind::SelfTy => todo!(),
|
|
||||||
TyKind::Path(_) => todo!(),
|
|
||||||
TyKind::Tuple(_) => todo!(),
|
|
||||||
TyKind::Ref(_) => todo!(),
|
|
||||||
TyKind::Fn(_) => todo!(),
|
|
||||||
},
|
|
||||||
ItemKind::Alias(_) => {}
|
|
||||||
ItemKind::Const(_) => todo!(),
|
|
||||||
ItemKind::Static(_) => todo!(),
|
|
||||||
ItemKind::Module(_) => todo!(),
|
|
||||||
ItemKind::Function(_) => {}
|
|
||||||
ItemKind::Struct(_) => {}
|
|
||||||
ItemKind::Enum(_) => {}
|
|
||||||
ItemKind::Impl(_) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_type(&self, kind: &TyKind) -> Option<DefID> {
|
|
||||||
match kind {
|
|
||||||
TyKind::Never => todo!(),
|
|
||||||
TyKind::Empty => todo!(),
|
|
||||||
TyKind::SelfTy => todo!(),
|
|
||||||
TyKind::Path(_) => todo!(),
|
|
||||||
TyKind::Tuple(_) => todo!(),
|
|
||||||
TyKind::Ref(_) => todo!(),
|
|
||||||
TyKind::Fn(_) => todo!(),
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod typeref {
|
|
||||||
//! Stores type and reference info
|
|
||||||
|
|
||||||
use crate::key::DefID;
|
|
||||||
|
|
||||||
/// The Type struct represents all valid types, and can be trivially equality-compared
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct TypeRef {
|
|
||||||
/// You can only have a pointer chain 65535 pointers long.
|
|
||||||
ref_depth: u16,
|
|
||||||
/// Types can be [Generic](RefKind::Generic) or [Concrete](RefKind::Concrete)
|
|
||||||
kind: RefKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Types can be [Generic](RefKind::Generic) or [Concrete](RefKind::Concrete)
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum RefKind {
|
|
||||||
/// A Concrete type has an associated [Def](super::definition::Def)
|
|
||||||
Concrete(DefID),
|
|
||||||
/// A Generic type is a *locally unique* comparable value,
|
|
||||||
/// valid only until the end of its typing context.
|
|
||||||
/// This is usually the surrounding function.
|
|
||||||
Generic(usize),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
/// What is an inference rule?
|
|
||||||
/// An inference rule is a specification with a set of predicates and a judgement
|
|
||||||
|
|
||||||
/// Let's give every type an ID
|
|
||||||
struct TypeID(usize);
|
|
||||||
|
|
||||||
/// Let's give every type some data:
|
|
||||||
|
|
||||||
struct TypeDef<'def> {
|
|
||||||
name: String,
|
|
||||||
definition: &'def Item,
|
|
||||||
}
|
|
||||||
|
|
||||||
and store them in a big vector of type descriptions:
|
|
||||||
|
|
||||||
struct TypeMap<'def> {
|
|
||||||
types: Vec<TypeDef<'def>>,
|
|
||||||
}
|
|
||||||
// todo: insertion of a type should yield a TypeID
|
|
||||||
// todo: impl index with TypeID
|
|
||||||
|
|
||||||
Let's store type information as either a concrete type or a generic type:
|
|
||||||
|
|
||||||
/// The Type struct represents all valid types, and can be trivially equality-compared
|
|
||||||
pub struct Type {
|
|
||||||
/// You can only have a pointer chain 65535 pointers long.
|
|
||||||
ref_depth: u16,
|
|
||||||
kind: TKind,
|
|
||||||
}
|
|
||||||
pub enum TKind {
|
|
||||||
Concrete(TypeID),
|
|
||||||
Generic(usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
And assume I can specify a rule based on its inputs and outputs:
|
|
||||||
|
|
||||||
Rule {
|
|
||||||
operation: If,
|
|
||||||
/// The inputs field is populated by
|
|
||||||
inputs: [Concrete(BOOL), Generic(0), Generic(0)],
|
|
||||||
outputs: Generic(0),
|
|
||||||
/// This rule is compiler-intrinsic!
|
|
||||||
through: None,
|
|
||||||
}
|
|
||||||
|
|
||||||
Rule {
|
|
||||||
operation: Add,
|
|
||||||
inputs: [Concrete(I32), Concrete(I32)],
|
|
||||||
outputs: Concrete(I32),
|
|
||||||
/// This rule is not compiler-intrinsic (it is overloaded!)
|
|
||||||
through: Some(&ImplAddForI32::Add),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
These rules can be stored in some kind of rule database:
|
|
||||||
|
|
||||||
let rules: Hashmap<Operation, Vec<Rule>> {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub mod rule {
|
|
||||||
use crate::{key::DefID, typeref::TypeRef};
|
|
||||||
|
|
||||||
pub struct Rule {
|
|
||||||
/// What is this Rule for?
|
|
||||||
pub operation: (),
|
|
||||||
/// What inputs does it take?
|
|
||||||
pub inputs: Vec<TypeRef>,
|
|
||||||
/// What output does it produce?
|
|
||||||
pub output: TypeRef,
|
|
||||||
/// Where did this rule come from?
|
|
||||||
pub through: Origin,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Genericize
|
|
||||||
pub enum Operation {
|
|
||||||
Mul,
|
|
||||||
Div,
|
|
||||||
Rem,
|
|
||||||
Add,
|
|
||||||
Sub,
|
|
||||||
|
|
||||||
Deref,
|
|
||||||
Neg,
|
|
||||||
Not,
|
|
||||||
At,
|
|
||||||
Tilde,
|
|
||||||
|
|
||||||
Index,
|
|
||||||
|
|
||||||
If,
|
|
||||||
While,
|
|
||||||
For,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Origin {
|
|
||||||
/// This rule is built into the compiler
|
|
||||||
Intrinsic,
|
|
||||||
/// This rule is derived from an implementation on a type
|
|
||||||
Extrinsic(DefID),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod typeck {
|
|
||||||
#![allow(unused)]
|
|
||||||
use cl_ast::*;
|
|
||||||
|
|
||||||
pub struct Context {
|
|
||||||
rules: (),
|
|
||||||
}
|
|
||||||
|
|
||||||
trait TypeCheck {}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
650
compiler/cl-ast/src/ast.rs
Normal file
650
compiler/cl-ast/src/ast.rs
Normal file
@@ -0,0 +1,650 @@
|
|||||||
|
//! # The Abstract Syntax Tree
|
||||||
|
//! Contains definitions of Conlang AST Nodes.
|
||||||
|
//!
|
||||||
|
//! # Notable nodes
|
||||||
|
//! - [Item] and [ItemKind]: Top-level constructs
|
||||||
|
//! - [Stmt] and [StmtKind]: Statements
|
||||||
|
//! - [Expr] and [ExprKind]: Expressions
|
||||||
|
//! - [Assign], [Modify], [Binary], and [Unary] expressions
|
||||||
|
//! - [ModifyKind], [BinaryKind], and [UnaryKind] operators
|
||||||
|
//! - [Ty] and [TyKind]: Type qualifiers
|
||||||
|
//! - [Pattern]: Pattern matching operators
|
||||||
|
//! - [Path]: Path expressions
|
||||||
|
use cl_structures::{intern::interned::Interned, span::*};
|
||||||
|
|
||||||
|
/// An [Interned] static [str], used in place of an identifier
|
||||||
|
pub type Sym = Interned<'static, str>;
|
||||||
|
|
||||||
|
/// Whether a binding ([Static] or [Let]) or reference is mutable or not
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Mutability {
|
||||||
|
#[default]
|
||||||
|
Not,
|
||||||
|
Mut,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether an [Item] is visible outside of the current [Module]
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Visibility {
|
||||||
|
#[default]
|
||||||
|
Private,
|
||||||
|
Public,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A list of [Item]s
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
|
||||||
|
pub struct File {
|
||||||
|
pub name: &'static str,
|
||||||
|
pub items: Vec<Item>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A list of [Meta] decorators
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Attrs {
|
||||||
|
pub meta: Vec<Meta>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A metadata decorator
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Meta {
|
||||||
|
pub name: Sym,
|
||||||
|
pub kind: MetaKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information attached to [Meta]data
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum MetaKind {
|
||||||
|
Plain,
|
||||||
|
Equals(Literal),
|
||||||
|
Func(Vec<Literal>),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Items
|
||||||
|
/// Anything that can appear at the top level of a [File]
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Item {
|
||||||
|
pub span: Span,
|
||||||
|
pub attrs: Attrs,
|
||||||
|
pub vis: Visibility,
|
||||||
|
pub kind: ItemKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// What kind of [Item] is this?
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum ItemKind {
|
||||||
|
// TODO: Trait declaration ("trait") item?
|
||||||
|
/// A [module](Module)
|
||||||
|
Module(Module),
|
||||||
|
/// A [type alias](Alias)
|
||||||
|
Alias(Alias),
|
||||||
|
/// An [enumerated type](Enum), with a discriminant and optional data
|
||||||
|
Enum(Enum),
|
||||||
|
/// A [structure](Struct)
|
||||||
|
Struct(Struct),
|
||||||
|
/// A [constant](Const)
|
||||||
|
Const(Const),
|
||||||
|
/// A [static](Static) variable
|
||||||
|
Static(Static),
|
||||||
|
/// A [function definition](Function)
|
||||||
|
Function(Function),
|
||||||
|
/// An [implementation](Impl)
|
||||||
|
Impl(Impl),
|
||||||
|
/// An [import](Use)
|
||||||
|
Use(Use),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A list of type variables to introduce
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Generics {
|
||||||
|
pub vars: Vec<Sym>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An ordered collection of [Items](Item)
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Module {
|
||||||
|
pub name: Sym,
|
||||||
|
pub file: Option<File>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An alias to another [Ty]
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Alias {
|
||||||
|
pub name: Sym,
|
||||||
|
pub from: Option<Box<Ty>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A compile-time constant
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Const {
|
||||||
|
pub name: Sym,
|
||||||
|
pub ty: Box<Ty>,
|
||||||
|
pub init: Box<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A `static` variable
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Static {
|
||||||
|
pub mutable: Mutability,
|
||||||
|
pub name: Sym,
|
||||||
|
pub ty: Box<Ty>,
|
||||||
|
pub init: Box<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Code, and the interface to that code
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Function {
|
||||||
|
pub name: Sym,
|
||||||
|
pub gens: Generics,
|
||||||
|
pub sign: TyFn,
|
||||||
|
pub bind: Pattern,
|
||||||
|
pub body: Option<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A user-defined product type
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Struct {
|
||||||
|
pub name: Sym,
|
||||||
|
pub gens: Generics,
|
||||||
|
pub kind: StructKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Either a [Struct]'s [StructMember]s or tuple [Ty]pes, if present.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum StructKind {
|
||||||
|
Empty,
|
||||||
|
Tuple(Vec<Ty>),
|
||||||
|
Struct(Vec<StructMember>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The [Visibility], [Sym], and [Ty]pe of a single [Struct] member
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct StructMember {
|
||||||
|
pub vis: Visibility,
|
||||||
|
pub name: Sym,
|
||||||
|
pub ty: Ty,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A user-defined sum type
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Enum {
|
||||||
|
pub name: Sym,
|
||||||
|
pub gens: Generics,
|
||||||
|
pub variants: Vec<Variant>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A single [Enum] variant
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Variant {
|
||||||
|
pub name: Sym,
|
||||||
|
pub kind: StructKind,
|
||||||
|
pub body: Option<Box<Expr>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sub-[items](Item) (associated functions, etc.) for a [Ty]
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Impl {
|
||||||
|
pub gens: Generics,
|
||||||
|
pub target: ImplKind,
|
||||||
|
pub body: File,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: `impl` Trait for <Target> { }
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum ImplKind {
|
||||||
|
Type(Ty),
|
||||||
|
Trait { impl_trait: Path, for_type: Box<Ty> },
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An import of nonlocal [Item]s
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Use {
|
||||||
|
pub absolute: bool,
|
||||||
|
pub tree: UseTree,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A tree of [Item] imports
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum UseTree {
|
||||||
|
Tree(Vec<UseTree>),
|
||||||
|
Path(PathPart, Box<UseTree>),
|
||||||
|
Alias(Sym, Sym),
|
||||||
|
Name(Sym),
|
||||||
|
Glob,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A type expression
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Ty {
|
||||||
|
pub span: Span,
|
||||||
|
pub kind: TyKind,
|
||||||
|
pub gens: Generics,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information about a [Ty]pe expression
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum TyKind {
|
||||||
|
Never,
|
||||||
|
Infer,
|
||||||
|
Path(Path),
|
||||||
|
Array(TyArray),
|
||||||
|
Slice(TySlice),
|
||||||
|
Tuple(TyTuple),
|
||||||
|
Ref(TyRef),
|
||||||
|
Ptr(TyPtr),
|
||||||
|
Fn(TyFn),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An array of [`T`](Ty)
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct TyArray {
|
||||||
|
pub ty: Box<Ty>,
|
||||||
|
pub count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [Ty]pe slice expression: `[T]`
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct TySlice {
|
||||||
|
pub ty: Box<Ty>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A tuple of [Ty]pes
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct TyTuple {
|
||||||
|
pub types: Vec<Ty>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [Ty]pe-reference expression as (number of `&`, [Path])
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct TyRef {
|
||||||
|
pub mutable: Mutability,
|
||||||
|
pub count: u16,
|
||||||
|
pub to: Box<Ty>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [Ty]pe-reference expression as (number of `&`, [Path])
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct TyPtr {
|
||||||
|
pub to: Box<Ty>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The args and return value for a function pointer [Ty]pe
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct TyFn {
|
||||||
|
pub args: Box<Ty>,
|
||||||
|
pub rety: Box<Ty>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A path to an [Item] in the [Module] tree
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Path {
|
||||||
|
pub absolute: bool,
|
||||||
|
pub parts: Vec<PathPart>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A single component of a [Path]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum PathPart {
|
||||||
|
SuperKw,
|
||||||
|
SelfTy,
|
||||||
|
Ident(Sym),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An abstract statement, and associated metadata
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Stmt {
|
||||||
|
pub span: Span,
|
||||||
|
pub kind: StmtKind,
|
||||||
|
pub semi: Semi,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the [Stmt] is a [Let], [Item], or [Expr] statement
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum StmtKind {
|
||||||
|
Empty,
|
||||||
|
Item(Box<Item>),
|
||||||
|
Expr(Box<Expr>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether or not a [Stmt] is followed by a semicolon
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Semi {
|
||||||
|
Terminated,
|
||||||
|
Unterminated,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An expression, the beating heart of the language
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Expr {
|
||||||
|
pub span: Span,
|
||||||
|
pub kind: ExprKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Any of the different [Expr]essions
|
||||||
|
#[derive(Clone, Default, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum ExprKind {
|
||||||
|
/// An empty expression: `(` `)`
|
||||||
|
#[default]
|
||||||
|
Empty,
|
||||||
|
/// A [Closure] expression: `|` [`Expr`] `|` ( -> [`Ty`])? [`Expr`]
|
||||||
|
Closure(Closure),
|
||||||
|
/// A [Tuple] expression: `(` [`Expr`] (`,` [`Expr`])+ `)`
|
||||||
|
Tuple(Tuple),
|
||||||
|
/// A [Struct creation](Structor) expression: [Path] `{` ([Fielder] `,`)* [Fielder]? `}`
|
||||||
|
Structor(Structor),
|
||||||
|
/// An [Array] literal: `[` [`Expr`] (`,` [`Expr`])\* `]`
|
||||||
|
Array(Array),
|
||||||
|
/// An Array literal constructed with [repeat syntax](ArrayRep)
|
||||||
|
/// `[` [Expr] `;` [Literal] `]`
|
||||||
|
ArrayRep(ArrayRep),
|
||||||
|
/// An address-of expression: `&` `mut`? [`Expr`]
|
||||||
|
AddrOf(AddrOf),
|
||||||
|
/// A backtick-quoted expression
|
||||||
|
Quote(Quote),
|
||||||
|
/// A [Literal]: 0x42, 1e123, 2.4, "Hello"
|
||||||
|
Literal(Literal),
|
||||||
|
/// A [Grouping](Group) expression `(` [`Expr`] `)`
|
||||||
|
Group(Group),
|
||||||
|
/// A [Block] expression: `{` [`Stmt`]\* [`Expr`]? `}`
|
||||||
|
Block(Block),
|
||||||
|
|
||||||
|
/// An [Assign]ment expression: [`Expr`] (`=` [`Expr`])\+
|
||||||
|
Assign(Assign),
|
||||||
|
/// A [Modify]-assignment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+
|
||||||
|
Modify(Modify),
|
||||||
|
/// A [Binary] expression: [`Expr`] ([`BinaryKind`] [`Expr`])\+
|
||||||
|
Binary(Binary),
|
||||||
|
/// A [Unary] expression: [`UnaryKind`]\* [`Expr`]
|
||||||
|
Unary(Unary),
|
||||||
|
/// A [Member] access expression: [`Expr`] [`MemberKind`]\*
|
||||||
|
Member(Member),
|
||||||
|
/// An Array [Index] expression: a[10, 20, 30]
|
||||||
|
Index(Index),
|
||||||
|
/// A [Cast] expression: [`Expr`] `as` [`Ty`]
|
||||||
|
Cast(Cast),
|
||||||
|
/// A [path expression](Path): `::`? [PathPart] (`::` [PathPart])*
|
||||||
|
Path(Path),
|
||||||
|
/// A local bind instruction, `let` [`Sym`] `=` [`Expr`]
|
||||||
|
Let(Let),
|
||||||
|
/// A [Match] expression: `match` [Expr] `{` ([MatchArm] `,`)* [MatchArm]? `}`
|
||||||
|
Match(Match),
|
||||||
|
/// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]?
|
||||||
|
While(While),
|
||||||
|
/// An [If] expression: `if` [`Expr`] [`Block`] [`Else`]?
|
||||||
|
If(If),
|
||||||
|
/// A [For] expression: `for` [`Pattern`] `in` [`Expr`] [`Block`] [`Else`]?
|
||||||
|
For(For),
|
||||||
|
/// A [Break] expression: `break` [`Expr`]?
|
||||||
|
Break(Break),
|
||||||
|
/// A [Return] expression `return` [`Expr`]?
|
||||||
|
Return(Return),
|
||||||
|
/// A continue expression: `continue`
|
||||||
|
Continue,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Closure [expression](Expr): `|` [`Expr`] `|` ( -> [`Ty`])? [`Expr`]
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Closure {
|
||||||
|
pub arg: Box<Pattern>,
|
||||||
|
pub body: Box<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [Tuple] expression: `(` [`Expr`] (`,` [`Expr`])+ `)`
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Tuple {
|
||||||
|
pub exprs: Vec<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [Struct creation](Structor) expression: [Path] `{` ([Fielder] `,`)* [Fielder]? `}`
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Structor {
|
||||||
|
pub to: Path,
|
||||||
|
pub init: Vec<Fielder>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [Struct field initializer] expression: [Sym] (`=` [Expr])?
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Fielder {
|
||||||
|
pub name: Sym,
|
||||||
|
pub init: Option<Box<Expr>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An [Array] literal: `[` [`Expr`] (`,` [`Expr`])\* `]`
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Array {
|
||||||
|
pub values: Vec<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An Array literal constructed with [repeat syntax](ArrayRep)
|
||||||
|
/// `[` [Expr] `;` [Literal] `]`
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct ArrayRep {
|
||||||
|
pub value: Box<Expr>,
|
||||||
|
pub repeat: Box<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An address-of expression: `&` `mut`? [`Expr`]
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct AddrOf {
|
||||||
|
pub mutable: Mutability,
|
||||||
|
pub expr: Box<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A cast expression: [`Expr`] `as` [`Ty`]
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Cast {
|
||||||
|
pub head: Box<Expr>,
|
||||||
|
pub ty: Ty,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A backtick-quoted subexpression-literal
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Quote {
|
||||||
|
pub quote: Box<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [Literal]: 0x42, 1e123, 2.4, "Hello"
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Literal {
|
||||||
|
Bool(bool),
|
||||||
|
Char(char),
|
||||||
|
Int(u128),
|
||||||
|
Float(u64),
|
||||||
|
String(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [Grouping](Group) expression `(` [`Expr`] `)`
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Group {
|
||||||
|
pub expr: Box<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [Block] expression: `{` [`Stmt`]\* [`Expr`]? `}`
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Block {
|
||||||
|
pub stmts: Vec<Stmt>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An [Assign]ment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Assign {
|
||||||
|
pub parts: Box<(Expr, Expr)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [Modify]-assignment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Modify {
|
||||||
|
pub kind: ModifyKind,
|
||||||
|
pub parts: Box<(Expr, Expr)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum ModifyKind {
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
|
Xor,
|
||||||
|
Shl,
|
||||||
|
Shr,
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Mul,
|
||||||
|
Div,
|
||||||
|
Rem,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [Binary] expression: [`Expr`] ([`BinaryKind`] [`Expr`])\+
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Binary {
|
||||||
|
pub kind: BinaryKind,
|
||||||
|
pub parts: Box<(Expr, Expr)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [Binary] operator
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum BinaryKind {
|
||||||
|
Lt,
|
||||||
|
LtEq,
|
||||||
|
Equal,
|
||||||
|
NotEq,
|
||||||
|
GtEq,
|
||||||
|
Gt,
|
||||||
|
RangeExc,
|
||||||
|
RangeInc,
|
||||||
|
LogAnd,
|
||||||
|
LogOr,
|
||||||
|
LogXor,
|
||||||
|
BitAnd,
|
||||||
|
BitOr,
|
||||||
|
BitXor,
|
||||||
|
Shl,
|
||||||
|
Shr,
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Mul,
|
||||||
|
Div,
|
||||||
|
Rem,
|
||||||
|
Call,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [Unary] expression: [`UnaryKind`]\* [`Expr`]
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Unary {
|
||||||
|
pub kind: UnaryKind,
|
||||||
|
pub tail: Box<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [Unary] operator
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum UnaryKind {
|
||||||
|
Deref,
|
||||||
|
Neg,
|
||||||
|
Not,
|
||||||
|
RangeInc,
|
||||||
|
RangeExc,
|
||||||
|
/// A Loop expression: `loop` [`Block`]
|
||||||
|
Loop,
|
||||||
|
/// Unused
|
||||||
|
At,
|
||||||
|
/// Unused
|
||||||
|
Tilde,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [Member] access expression: [`Expr`] [`MemberKind`]\*
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Member {
|
||||||
|
pub head: Box<Expr>,
|
||||||
|
pub kind: MemberKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The kind of [Member] access
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum MemberKind {
|
||||||
|
Call(Sym, Tuple),
|
||||||
|
Struct(Sym),
|
||||||
|
Tuple(Literal),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A repeated [Index] expression: a[10, 20, 30][40, 50, 60]
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Index {
|
||||||
|
pub head: Box<Expr>,
|
||||||
|
pub indices: Vec<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A local variable declaration [Stmt]
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Let {
|
||||||
|
pub mutable: Mutability,
|
||||||
|
pub name: Pattern,
|
||||||
|
pub ty: Option<Box<Ty>>,
|
||||||
|
pub init: Option<Box<Expr>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A `match` expression: `match` `{` ([MatchArm] `,`)* [MatchArm]? `}`
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Match {
|
||||||
|
pub scrutinee: Box<Expr>,
|
||||||
|
pub arms: Vec<MatchArm>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A single arm of a [Match] expression: [`Pattern`] `=>` [`Expr`]
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct MatchArm(pub Pattern, pub Expr);
|
||||||
|
|
||||||
|
/// A [Pattern] meta-expression (any [`ExprKind`] that fits pattern rules)
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Pattern {
|
||||||
|
Name(Sym),
|
||||||
|
Path(Path),
|
||||||
|
Literal(Literal),
|
||||||
|
Rest(Option<Box<Pattern>>),
|
||||||
|
Ref(Mutability, Box<Pattern>),
|
||||||
|
RangeExc(Box<Pattern>, Box<Pattern>),
|
||||||
|
RangeInc(Box<Pattern>, Box<Pattern>),
|
||||||
|
Tuple(Vec<Pattern>),
|
||||||
|
Array(Vec<Pattern>),
|
||||||
|
Struct(Path, Vec<(Sym, Option<Pattern>)>),
|
||||||
|
TupleStruct(Path, Vec<Pattern>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]?
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct While {
|
||||||
|
pub cond: Box<Expr>,
|
||||||
|
pub pass: Box<Block>,
|
||||||
|
pub fail: Else,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An [If] expression: `if` [`Expr`] [`Block`] [`Else`]?
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct If {
|
||||||
|
pub cond: Box<Expr>,
|
||||||
|
pub pass: Box<Block>,
|
||||||
|
pub fail: Else,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [For] expression: `for` Pattern `in` [`Expr`] [`Block`] [`Else`]?
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct For {
|
||||||
|
pub bind: Pattern,
|
||||||
|
pub cond: Box<Expr>,
|
||||||
|
pub pass: Box<Block>,
|
||||||
|
pub fail: Else,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The (optional) `else` clause of a [While], [If], or [For] expression
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Else {
|
||||||
|
pub body: Option<Box<Expr>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [Break] expression: `break` [`Expr`]?
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Break {
|
||||||
|
pub body: Option<Box<Expr>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [Return] expression `return` [`Expr`]?
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Return {
|
||||||
|
pub body: Option<Box<Expr>>,
|
||||||
|
}
|
||||||
8
compiler/cl-ast/src/ast_impl.rs
Normal file
8
compiler/cl-ast/src/ast_impl.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
//! Implementations of AST nodes and traits
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
mod convert;
|
||||||
|
mod display;
|
||||||
|
mod path;
|
||||||
|
|
||||||
|
pub(crate) mod weight_of;
|
||||||
162
compiler/cl-ast/src/ast_impl/convert.rs
Normal file
162
compiler/cl-ast/src/ast_impl/convert.rs
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
//! Converts between major enums and enum variants
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl<T: AsRef<str>> From<T> for PathPart {
|
||||||
|
fn from(value: T) -> Self {
|
||||||
|
match value.as_ref() {
|
||||||
|
"super" => PathPart::SuperKw,
|
||||||
|
ident => PathPart::Ident(ident.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro impl_from ($(impl From for $T:ty {$($from:ty => $to:expr),*$(,)?})*) {$($(
|
||||||
|
impl From<$from> for $T {
|
||||||
|
fn from(value: $from) -> Self {
|
||||||
|
$to(value.into()) // Uses *tuple constructor*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<Box<$from>> for $T {
|
||||||
|
fn from(value: Box<$from>) -> Self {
|
||||||
|
$to((*value).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*)*}
|
||||||
|
|
||||||
|
impl_from! {
|
||||||
|
impl From for ItemKind {
|
||||||
|
Alias => ItemKind::Alias,
|
||||||
|
Const => ItemKind::Const,
|
||||||
|
Static => ItemKind::Static,
|
||||||
|
Module => ItemKind::Module,
|
||||||
|
Function => ItemKind::Function,
|
||||||
|
Struct => ItemKind::Struct,
|
||||||
|
Enum => ItemKind::Enum,
|
||||||
|
Impl => ItemKind::Impl,
|
||||||
|
Use => ItemKind::Use,
|
||||||
|
}
|
||||||
|
impl From for StructKind {
|
||||||
|
Vec<Ty> => StructKind::Tuple,
|
||||||
|
// TODO: Struct members in struct
|
||||||
|
}
|
||||||
|
impl From for TyKind {
|
||||||
|
Path => TyKind::Path,
|
||||||
|
TyTuple => TyKind::Tuple,
|
||||||
|
TyRef => TyKind::Ref,
|
||||||
|
TyPtr => TyKind::Ptr,
|
||||||
|
TyFn => TyKind::Fn,
|
||||||
|
}
|
||||||
|
impl From for StmtKind {
|
||||||
|
Item => StmtKind::Item,
|
||||||
|
Expr => StmtKind::Expr,
|
||||||
|
}
|
||||||
|
impl From for ExprKind {
|
||||||
|
Let => ExprKind::Let,
|
||||||
|
Closure => ExprKind::Closure,
|
||||||
|
Quote => ExprKind::Quote,
|
||||||
|
Match => ExprKind::Match,
|
||||||
|
Assign => ExprKind::Assign,
|
||||||
|
Modify => ExprKind::Modify,
|
||||||
|
Binary => ExprKind::Binary,
|
||||||
|
Unary => ExprKind::Unary,
|
||||||
|
Cast => ExprKind::Cast,
|
||||||
|
Member => ExprKind::Member,
|
||||||
|
Index => ExprKind::Index,
|
||||||
|
Path => ExprKind::Path,
|
||||||
|
Literal => ExprKind::Literal,
|
||||||
|
Array => ExprKind::Array,
|
||||||
|
ArrayRep => ExprKind::ArrayRep,
|
||||||
|
AddrOf => ExprKind::AddrOf,
|
||||||
|
Block => ExprKind::Block,
|
||||||
|
Group => ExprKind::Group,
|
||||||
|
Tuple => ExprKind::Tuple,
|
||||||
|
While => ExprKind::While,
|
||||||
|
If => ExprKind::If,
|
||||||
|
For => ExprKind::For,
|
||||||
|
Break => ExprKind::Break,
|
||||||
|
Return => ExprKind::Return,
|
||||||
|
}
|
||||||
|
impl From for Literal {
|
||||||
|
bool => Literal::Bool,
|
||||||
|
char => Literal::Char,
|
||||||
|
u128 => Literal::Int,
|
||||||
|
String => Literal::String,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Option<Expr>> for Else {
|
||||||
|
fn from(value: Option<Expr>) -> Self {
|
||||||
|
Self { body: value.map(Into::into) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<Expr> for Else {
|
||||||
|
fn from(value: Expr) -> Self {
|
||||||
|
Self { body: Some(value.into()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Expr> for Pattern {
|
||||||
|
type Error = Expr;
|
||||||
|
|
||||||
|
/// Performs the conversion. On failure, returns the *first* non-pattern subexpression.
|
||||||
|
fn try_from(value: Expr) -> Result<Self, Self::Error> {
|
||||||
|
Ok(match value.kind {
|
||||||
|
ExprKind::Literal(literal) => Pattern::Literal(literal),
|
||||||
|
ExprKind::Path(Path { absolute: false, ref parts }) => match parts.as_slice() {
|
||||||
|
[PathPart::Ident(name)] => Pattern::Name(*name),
|
||||||
|
_ => Err(value)?,
|
||||||
|
},
|
||||||
|
ExprKind::Empty => Pattern::Tuple(vec![]),
|
||||||
|
ExprKind::Group(Group { expr }) => Pattern::Tuple(vec![Pattern::try_from(*expr)?]),
|
||||||
|
ExprKind::Tuple(Tuple { exprs }) => Pattern::Tuple(
|
||||||
|
exprs
|
||||||
|
.into_iter()
|
||||||
|
.map(Pattern::try_from)
|
||||||
|
.collect::<Result<_, _>>()?,
|
||||||
|
),
|
||||||
|
ExprKind::AddrOf(AddrOf { mutable, expr }) => {
|
||||||
|
Pattern::Ref(mutable, Box::new(Pattern::try_from(*expr)?))
|
||||||
|
}
|
||||||
|
ExprKind::Array(Array { values }) => Pattern::Array(
|
||||||
|
values
|
||||||
|
.into_iter()
|
||||||
|
.map(Pattern::try_from)
|
||||||
|
.collect::<Result<_, _>>()?,
|
||||||
|
),
|
||||||
|
ExprKind::Binary(Binary { kind: BinaryKind::Call, parts }) => {
|
||||||
|
let (Expr { kind: ExprKind::Path(path), .. }, args) = *parts else {
|
||||||
|
return Err(parts.0);
|
||||||
|
};
|
||||||
|
match args.kind {
|
||||||
|
ExprKind::Empty | ExprKind::Tuple(_) => {}
|
||||||
|
_ => return Err(args),
|
||||||
|
}
|
||||||
|
let Pattern::Tuple(args) = Pattern::try_from(args)? else {
|
||||||
|
unreachable!("Arguments should be convertible to pattern!")
|
||||||
|
};
|
||||||
|
Pattern::TupleStruct(path, args)
|
||||||
|
}
|
||||||
|
ExprKind::Binary(Binary { kind: BinaryKind::RangeExc, parts }) => {
|
||||||
|
let (head, tail) = (Pattern::try_from(parts.0)?, Pattern::try_from(parts.1)?);
|
||||||
|
Pattern::RangeExc(head.into(), tail.into())
|
||||||
|
}
|
||||||
|
ExprKind::Binary(Binary { kind: BinaryKind::RangeInc, parts }) => {
|
||||||
|
let (head, tail) = (Pattern::try_from(parts.0)?, Pattern::try_from(parts.1)?);
|
||||||
|
Pattern::RangeInc(head.into(), tail.into())
|
||||||
|
}
|
||||||
|
ExprKind::Unary(Unary { kind: UnaryKind::RangeExc, tail }) => {
|
||||||
|
Pattern::Rest(Some(Pattern::try_from(*tail)?.into()))
|
||||||
|
}
|
||||||
|
ExprKind::Structor(Structor { to, init }) => {
|
||||||
|
let fields = init
|
||||||
|
.into_iter()
|
||||||
|
.map(|Fielder { name, init }| {
|
||||||
|
Ok((name, init.map(|i| Pattern::try_from(*i)).transpose()?))
|
||||||
|
})
|
||||||
|
.collect::<Result<_, Self::Error>>()?;
|
||||||
|
Pattern::Struct(to, fields)
|
||||||
|
}
|
||||||
|
_ => Err(value)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
773
compiler/cl-ast/src/ast_impl/display.rs
Normal file
773
compiler/cl-ast/src/ast_impl/display.rs
Normal file
@@ -0,0 +1,773 @@
|
|||||||
|
//! Implements [Display] for [AST](super::super) Types
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use format::{delimiters::*, *};
|
||||||
|
use std::{
|
||||||
|
borrow::Borrow,
|
||||||
|
fmt::{Display, Write},
|
||||||
|
};
|
||||||
|
|
||||||
|
fn separate<I: Display, W: Write>(
|
||||||
|
iterable: impl IntoIterator<Item = I>,
|
||||||
|
sep: &'static str,
|
||||||
|
) -> impl FnOnce(W) -> std::fmt::Result {
|
||||||
|
move |mut f| {
|
||||||
|
for (idx, item) in iterable.into_iter().enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
f.write_str(sep)?;
|
||||||
|
}
|
||||||
|
write!(f, "{item}")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Mutability {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Mutability::Not => Ok(()),
|
||||||
|
Mutability::Mut => "mut ".fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Visibility {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Visibility::Private => Ok(()),
|
||||||
|
Visibility::Public => "pub ".fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Literal {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Literal::Bool(v) => v.fmt(f),
|
||||||
|
Literal::Char(v) => write!(f, "'{}'", v.escape_debug()),
|
||||||
|
Literal::Int(v) => v.fmt(f),
|
||||||
|
Literal::Float(v) => write!(f, "{:?}", f64::from_bits(*v)),
|
||||||
|
Literal::String(v) => write!(f, "\"{}\"", v.escape_debug()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for File {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
separate(&self.items, "\n\n")(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Attrs {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { meta } = self;
|
||||||
|
if meta.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
"#".fmt(f)?;
|
||||||
|
separate(meta, ", ")(&mut f.delimit(INLINE_SQUARE))?;
|
||||||
|
"\n".fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Meta {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { name, kind } = self;
|
||||||
|
write!(f, "{name}{kind}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for MetaKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
MetaKind::Plain => Ok(()),
|
||||||
|
MetaKind::Equals(v) => write!(f, " = {v}"),
|
||||||
|
MetaKind::Func(args) => separate(args, ", ")(f.delimit(INLINE_PARENS)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Item {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { span: _, attrs, vis, kind } = self;
|
||||||
|
attrs.fmt(f)?;
|
||||||
|
vis.fmt(f)?;
|
||||||
|
kind.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ItemKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
ItemKind::Alias(v) => v.fmt(f),
|
||||||
|
ItemKind::Const(v) => v.fmt(f),
|
||||||
|
ItemKind::Static(v) => v.fmt(f),
|
||||||
|
ItemKind::Module(v) => v.fmt(f),
|
||||||
|
ItemKind::Function(v) => v.fmt(f),
|
||||||
|
ItemKind::Struct(v) => v.fmt(f),
|
||||||
|
ItemKind::Enum(v) => v.fmt(f),
|
||||||
|
ItemKind::Impl(v) => v.fmt(f),
|
||||||
|
ItemKind::Use(v) => v.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Generics {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Generics { vars } = self;
|
||||||
|
if !vars.is_empty() {
|
||||||
|
separate(vars, ", ")(f.delimit_with("<", ">"))?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Alias {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { name, from } = self;
|
||||||
|
match from {
|
||||||
|
Some(from) => write!(f, "type {name} = {from};"),
|
||||||
|
None => write!(f, "type {name};"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Const {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { name, ty, init } = self;
|
||||||
|
write!(f, "const {name}: {ty} = {init}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Static {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { mutable, name, ty, init } = self;
|
||||||
|
write!(f, "static {mutable}{name}: {ty} = {init}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Module {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { name, file } = self;
|
||||||
|
write!(f, "mod {name}")?;
|
||||||
|
match file {
|
||||||
|
Some(items) => {
|
||||||
|
' '.fmt(f)?;
|
||||||
|
write!(f.delimit(BRACES), "{items}")
|
||||||
|
}
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Function {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { name, gens, sign: sign @ TyFn { args, rety }, bind, body } = self;
|
||||||
|
let types = match args.kind {
|
||||||
|
TyKind::Tuple(TyTuple { ref types }) => types.as_slice(),
|
||||||
|
_ => {
|
||||||
|
write!(f, "Invalid function signature: {sign}")?;
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let bind = match bind {
|
||||||
|
Pattern::Tuple(patterns) => patterns.as_slice(),
|
||||||
|
_ => {
|
||||||
|
write!(f, "Invalid argument binder: {bind}")?;
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
debug_assert_eq!(bind.len(), types.len());
|
||||||
|
write!(f, "fn {name}{gens} ")?;
|
||||||
|
{
|
||||||
|
let mut f = f.delimit(INLINE_PARENS);
|
||||||
|
|
||||||
|
for (idx, (arg, ty)) in bind.iter().zip(types.iter()).enumerate() {
|
||||||
|
if idx != 0 {
|
||||||
|
f.write_str(", ")?;
|
||||||
|
}
|
||||||
|
write!(f, "{arg}: {ty}")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let TyKind::Tuple(TyTuple { types }) = &rety.kind
|
||||||
|
&& !types.as_slice().is_empty()
|
||||||
|
{
|
||||||
|
write!(f, " -> {rety}")?
|
||||||
|
}
|
||||||
|
|
||||||
|
match body {
|
||||||
|
Some(body) => write!(f, " {body}"),
|
||||||
|
None => ';'.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Struct {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { name, gens, kind } = self;
|
||||||
|
write!(f, "struct {name}{gens}{kind}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for StructKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
StructKind::Empty => ';'.fmt(f),
|
||||||
|
StructKind::Tuple(v) => separate(v, ", ")(f.delimit(INLINE_PARENS)),
|
||||||
|
StructKind::Struct(v) => separate(v, ",\n")(f.delimit(SPACED_BRACES)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for StructMember {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { vis, name, ty } = self;
|
||||||
|
write!(f, "{vis}{name}: {ty}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Enum {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { name, gens, variants } = self;
|
||||||
|
write!(f, "enum {name}{gens}")?;
|
||||||
|
separate(variants, ",\n")(f.delimit(SPACED_BRACES))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Variant {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { name, kind, body } = self;
|
||||||
|
write!(f, "{name}{kind}")?;
|
||||||
|
match body {
|
||||||
|
Some(body) => write!(f, " {body}"),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Impl {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { gens, target, body } = self;
|
||||||
|
write!(f, "impl{gens} {target} ")?;
|
||||||
|
write!(f.delimit(BRACES), "{body}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ImplKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
ImplKind::Type(t) => t.fmt(f),
|
||||||
|
ImplKind::Trait { impl_trait, for_type } => {
|
||||||
|
write!(f, "{impl_trait} for {for_type}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Use {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { absolute, tree } = self;
|
||||||
|
f.write_str(if *absolute { "use ::" } else { "use " })?;
|
||||||
|
write!(f, "{tree};")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for UseTree {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
UseTree::Tree(tree) => separate(tree, ", ")(f.delimit(INLINE_BRACES)),
|
||||||
|
UseTree::Path(path, rest) => write!(f, "{path}::{rest}"),
|
||||||
|
UseTree::Alias(path, name) => write!(f, "{path} as {name}"),
|
||||||
|
UseTree::Name(name) => write!(f, "{name}"),
|
||||||
|
UseTree::Glob => write!(f, "*"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Ty {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { span: _, kind, gens } = self;
|
||||||
|
write!(f, "{kind}{gens}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for TyKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
TyKind::Never => "!".fmt(f),
|
||||||
|
TyKind::Infer => "_".fmt(f),
|
||||||
|
TyKind::Path(v) => v.fmt(f),
|
||||||
|
TyKind::Array(v) => v.fmt(f),
|
||||||
|
TyKind::Slice(v) => v.fmt(f),
|
||||||
|
TyKind::Tuple(v) => v.fmt(f),
|
||||||
|
TyKind::Ref(v) => v.fmt(f),
|
||||||
|
TyKind::Ptr(v) => v.fmt(f),
|
||||||
|
TyKind::Fn(v) => v.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for TyArray {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { ty, count } = self;
|
||||||
|
write!(f, "[{ty}; {count}]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for TySlice {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { ty } = self;
|
||||||
|
write!(f, "[{ty}]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for TyTuple {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
separate(&self.types, ", ")(f.delimit(INLINE_PARENS))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for TyRef {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let &Self { count, mutable, ref to } = self;
|
||||||
|
for _ in 0..count {
|
||||||
|
f.write_char('&')?;
|
||||||
|
}
|
||||||
|
write!(f, "{mutable}{to}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for TyPtr {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { to } = self;
|
||||||
|
write!(f, "*{to}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for TyFn {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { args, rety } = self;
|
||||||
|
write!(f, "fn {args}")?;
|
||||||
|
if let TyKind::Tuple(TyTuple { types }) = &rety.kind
|
||||||
|
&& !types.as_slice().is_empty()
|
||||||
|
{
|
||||||
|
write!(f, " -> {rety}")?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Path {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { absolute, parts } = self;
|
||||||
|
if *absolute {
|
||||||
|
"::".fmt(f)?;
|
||||||
|
}
|
||||||
|
separate(parts, "::")(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for PathPart {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
PathPart::SuperKw => "super".fmt(f),
|
||||||
|
PathPart::SelfTy => "Self".fmt(f),
|
||||||
|
PathPart::Ident(id) => id.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Stmt {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Stmt { span: _, kind, semi } = self;
|
||||||
|
write!(f, "{kind}{semi}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for StmtKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
StmtKind::Empty => Ok(()),
|
||||||
|
StmtKind::Item(v) => v.fmt(f),
|
||||||
|
StmtKind::Expr(v) => v.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Semi {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Semi::Terminated => ';'.fmt(f),
|
||||||
|
Semi::Unterminated => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Expr {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.kind.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ExprKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
ExprKind::Empty => "()".fmt(f),
|
||||||
|
ExprKind::Closure(v) => v.fmt(f),
|
||||||
|
ExprKind::Quote(v) => v.fmt(f),
|
||||||
|
ExprKind::Let(v) => v.fmt(f),
|
||||||
|
ExprKind::Match(v) => v.fmt(f),
|
||||||
|
ExprKind::Assign(v) => v.fmt(f),
|
||||||
|
ExprKind::Modify(v) => v.fmt(f),
|
||||||
|
ExprKind::Binary(v) => v.fmt(f),
|
||||||
|
ExprKind::Unary(v) => v.fmt(f),
|
||||||
|
ExprKind::Cast(v) => v.fmt(f),
|
||||||
|
ExprKind::Member(v) => v.fmt(f),
|
||||||
|
ExprKind::Index(v) => v.fmt(f),
|
||||||
|
ExprKind::Structor(v) => v.fmt(f),
|
||||||
|
ExprKind::Path(v) => v.fmt(f),
|
||||||
|
ExprKind::Literal(v) => v.fmt(f),
|
||||||
|
ExprKind::Array(v) => v.fmt(f),
|
||||||
|
ExprKind::ArrayRep(v) => v.fmt(f),
|
||||||
|
ExprKind::AddrOf(v) => v.fmt(f),
|
||||||
|
ExprKind::Block(v) => v.fmt(f),
|
||||||
|
ExprKind::Group(v) => v.fmt(f),
|
||||||
|
ExprKind::Tuple(v) => v.fmt(f),
|
||||||
|
ExprKind::While(v) => v.fmt(f),
|
||||||
|
ExprKind::If(v) => v.fmt(f),
|
||||||
|
ExprKind::For(v) => v.fmt(f),
|
||||||
|
ExprKind::Break(v) => v.fmt(f),
|
||||||
|
ExprKind::Return(v) => v.fmt(f),
|
||||||
|
ExprKind::Continue => "continue".fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Closure {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { arg, body } = self;
|
||||||
|
match arg.as_ref() {
|
||||||
|
Pattern::Tuple(args) => separate(args, ", ")(f.delimit_with("|", "|")),
|
||||||
|
_ => arg.fmt(f),
|
||||||
|
}?;
|
||||||
|
write!(f, " {body}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Quote {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { quote } = self;
|
||||||
|
write!(f, "`{quote}`")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Let {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { mutable, name, ty, init } = self;
|
||||||
|
write!(f, "let {mutable}{name}")?;
|
||||||
|
if let Some(value) = ty {
|
||||||
|
write!(f, ": {value}")?;
|
||||||
|
}
|
||||||
|
if let Some(value) = init {
|
||||||
|
write!(f, " = {value}")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Pattern {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Pattern::Name(sym) => sym.fmt(f),
|
||||||
|
Pattern::Path(path) => path.fmt(f),
|
||||||
|
Pattern::Literal(literal) => literal.fmt(f),
|
||||||
|
Pattern::Rest(Some(name)) => write!(f, "..{name}"),
|
||||||
|
Pattern::Rest(None) => "..".fmt(f),
|
||||||
|
Pattern::Ref(mutability, pattern) => write!(f, "&{mutability}{pattern}"),
|
||||||
|
Pattern::RangeExc(head, tail) => write!(f, "{head}..{tail}"),
|
||||||
|
Pattern::RangeInc(head, tail) => write!(f, "{head}..={tail}"),
|
||||||
|
Pattern::Tuple(patterns) => separate(patterns, ", ")(f.delimit(INLINE_PARENS)),
|
||||||
|
Pattern::Array(patterns) => separate(patterns, ", ")(f.delimit(INLINE_SQUARE)),
|
||||||
|
Pattern::Struct(path, items) => {
|
||||||
|
write!(f, "{path} ")?;
|
||||||
|
let f = &mut f.delimit(INLINE_BRACES);
|
||||||
|
for (idx, (name, item)) in items.iter().enumerate() {
|
||||||
|
if idx != 0 {
|
||||||
|
f.write_str(", ")?;
|
||||||
|
}
|
||||||
|
write!(f, "{name}")?;
|
||||||
|
if let Some(pattern) = item {
|
||||||
|
write!(f, ": {pattern}")?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Pattern::TupleStruct(path, items) => {
|
||||||
|
write!(f, "{path}")?;
|
||||||
|
separate(items, ", ")(f.delimit(INLINE_PARENS))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Match {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { scrutinee, arms } = self;
|
||||||
|
write!(f, "match {scrutinee} ")?;
|
||||||
|
separate(arms, ",\n")(f.delimit(BRACES))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for MatchArm {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self(pat, expr) = self;
|
||||||
|
write!(f, "{pat} => {expr}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Assign {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { parts } = self;
|
||||||
|
write!(f, "{} = {}", parts.0, parts.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Modify {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { kind, parts } = self;
|
||||||
|
write!(f, "{} {kind} {}", parts.0, parts.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ModifyKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
ModifyKind::Mul => "*=",
|
||||||
|
ModifyKind::Div => "/=",
|
||||||
|
ModifyKind::Rem => "%=",
|
||||||
|
ModifyKind::Add => "+=",
|
||||||
|
ModifyKind::Sub => "-=",
|
||||||
|
ModifyKind::And => "&=",
|
||||||
|
ModifyKind::Or => "|=",
|
||||||
|
ModifyKind::Xor => "^=",
|
||||||
|
ModifyKind::Shl => "<<=",
|
||||||
|
ModifyKind::Shr => ">>=",
|
||||||
|
}
|
||||||
|
.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Binary {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { kind, parts } = self;
|
||||||
|
let (head, tail) = parts.borrow();
|
||||||
|
match kind {
|
||||||
|
BinaryKind::Call => write!(f, "{head}{tail}"),
|
||||||
|
_ => write!(f, "{head} {kind} {tail}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for BinaryKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
BinaryKind::Lt => "<",
|
||||||
|
BinaryKind::LtEq => "<=",
|
||||||
|
BinaryKind::Equal => "==",
|
||||||
|
BinaryKind::NotEq => "!=",
|
||||||
|
BinaryKind::GtEq => ">=",
|
||||||
|
BinaryKind::Gt => ">",
|
||||||
|
BinaryKind::RangeExc => "..",
|
||||||
|
BinaryKind::RangeInc => "..=",
|
||||||
|
BinaryKind::LogAnd => "&&",
|
||||||
|
BinaryKind::LogOr => "||",
|
||||||
|
BinaryKind::LogXor => "^^",
|
||||||
|
BinaryKind::BitAnd => "&",
|
||||||
|
BinaryKind::BitOr => "|",
|
||||||
|
BinaryKind::BitXor => "^",
|
||||||
|
BinaryKind::Shl => "<<",
|
||||||
|
BinaryKind::Shr => ">>",
|
||||||
|
BinaryKind::Add => "+",
|
||||||
|
BinaryKind::Sub => "-",
|
||||||
|
BinaryKind::Mul => "*",
|
||||||
|
BinaryKind::Div => "/",
|
||||||
|
BinaryKind::Rem => "%",
|
||||||
|
BinaryKind::Call => "()",
|
||||||
|
}
|
||||||
|
.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Unary {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { kind, tail } = self;
|
||||||
|
write!(f, "{kind}{tail}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for UnaryKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
UnaryKind::Loop => "loop ",
|
||||||
|
UnaryKind::Deref => "*",
|
||||||
|
UnaryKind::Neg => "-",
|
||||||
|
UnaryKind::Not => "!",
|
||||||
|
UnaryKind::RangeExc => "..",
|
||||||
|
UnaryKind::RangeInc => "..=",
|
||||||
|
UnaryKind::At => "@",
|
||||||
|
UnaryKind::Tilde => "~",
|
||||||
|
}
|
||||||
|
.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Cast {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { head, ty } = self;
|
||||||
|
write!(f, "{head} as {ty}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Member {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { head, kind } = self;
|
||||||
|
write!(f, "{head}.{kind}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for MemberKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
MemberKind::Call(name, args) => write!(f, "{name}{args}"),
|
||||||
|
MemberKind::Struct(name) => write!(f, "{name}"),
|
||||||
|
MemberKind::Tuple(name) => write!(f, "{name}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Index {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { head, indices } = self;
|
||||||
|
write!(f, "{head}")?;
|
||||||
|
separate(indices, ", ")(f.delimit(INLINE_SQUARE))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Structor {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { to, init } = self;
|
||||||
|
write!(f, "{to} ")?;
|
||||||
|
separate(init, ", ")(f.delimit(INLINE_BRACES))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Fielder {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { name, init } = self;
|
||||||
|
write!(f, "{name}")?;
|
||||||
|
if let Some(init) = init {
|
||||||
|
write!(f, ": {init}")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Array {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
separate(&self.values, ", ")(f.delimit(INLINE_SQUARE))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ArrayRep {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { value, repeat } = self;
|
||||||
|
write!(f, "[{value}; {repeat}]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for AddrOf {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { mutable, expr } = self;
|
||||||
|
write!(f, "&{mutable}{expr}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Block {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { stmts } = self;
|
||||||
|
|
||||||
|
match stmts.as_slice() {
|
||||||
|
[] => "{}".fmt(f),
|
||||||
|
stmts => separate(stmts, "\n")(f.delimit(BRACES)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Group {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "({})", self.expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Tuple {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { exprs } = self;
|
||||||
|
|
||||||
|
match exprs.as_slice() {
|
||||||
|
[] => write!(f, "()"),
|
||||||
|
[expr] => write!(f, "({expr},)"),
|
||||||
|
exprs => separate(exprs, ", ")(f.delimit(INLINE_PARENS)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for While {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { cond, pass, fail } = self;
|
||||||
|
write!(f, "while {cond} {pass}{fail}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for If {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { cond, pass, fail } = self;
|
||||||
|
write!(f, "if {cond} {pass}{fail}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for For {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { bind, cond, pass, fail } = self;
|
||||||
|
write!(f, "for {bind} in {cond} {pass}{fail}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Else {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match &self.body {
|
||||||
|
Some(body) => write!(f, " else {body}"),
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Break {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "break")?;
|
||||||
|
match &self.body {
|
||||||
|
Some(body) => write!(f, " {body}"),
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Return {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "return")?;
|
||||||
|
match &self.body {
|
||||||
|
Some(body) => write!(f, " {body}"),
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
60
compiler/cl-ast/src/ast_impl/path.rs
Normal file
60
compiler/cl-ast/src/ast_impl/path.rs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
//! Utils for [Path]
|
||||||
|
use crate::{PathPart, Sym, ast::Path};
|
||||||
|
|
||||||
|
impl Path {
|
||||||
|
/// Appends a [PathPart] to this [Path]
|
||||||
|
pub fn push(&mut self, part: PathPart) {
|
||||||
|
self.parts.push(part);
|
||||||
|
}
|
||||||
|
/// Removes a [PathPart] from this [Path]
|
||||||
|
pub fn pop(&mut self) -> Option<PathPart> {
|
||||||
|
self.parts.pop()
|
||||||
|
}
|
||||||
|
/// Concatenates `self::other`. If `other` is an absolute [Path],
|
||||||
|
/// this replaces `self` with `other`
|
||||||
|
pub fn concat(mut self, other: &Self) -> Self {
|
||||||
|
if other.absolute {
|
||||||
|
other.clone()
|
||||||
|
} else {
|
||||||
|
self.parts.extend(other.parts.iter().cloned());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the defining [Sym] of this path
|
||||||
|
pub fn as_sym(&self) -> Option<Sym> {
|
||||||
|
match self.parts.as_slice() {
|
||||||
|
[.., PathPart::Ident(name)] => Some(*name),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks whether this path ends in the given [Sym]
|
||||||
|
pub fn ends_with(&self, name: &str) -> bool {
|
||||||
|
match self.parts.as_slice() {
|
||||||
|
[.., PathPart::Ident(last)] => name == &**last,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks whether this path refers to the sinkhole identifier, `_`
|
||||||
|
pub fn is_sinkhole(&self) -> bool {
|
||||||
|
if let [PathPart::Ident(id)] = self.parts.as_slice()
|
||||||
|
&& let "_" = id.to_ref()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PathPart {
|
||||||
|
pub fn from_sym(ident: Sym) -> Self {
|
||||||
|
Self::Ident(ident)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Sym> for Path {
|
||||||
|
fn from(value: Sym) -> Self {
|
||||||
|
Self { parts: vec![PathPart::Ident(value)], absolute: false }
|
||||||
|
}
|
||||||
|
}
|
||||||
617
compiler/cl-ast/src/ast_impl/weight_of.rs
Normal file
617
compiler/cl-ast/src/ast_impl/weight_of.rs
Normal file
@@ -0,0 +1,617 @@
|
|||||||
|
//! Approximates the size of an AST
|
||||||
|
|
||||||
|
use std::mem::size_of_val;
|
||||||
|
|
||||||
|
use crate::ast::*;
|
||||||
|
use cl_structures::{intern::interned::Interned, span::Span};
|
||||||
|
|
||||||
|
/// Approximates the size of an AST without including indirection (pointers) or padding
|
||||||
|
pub trait WeightOf {
|
||||||
|
/// Approximates the size of a syntax tree without including pointer/indirection or padding.
|
||||||
|
fn weight_of(&self) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for File {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { name, items } = self;
|
||||||
|
name.weight_of() + items.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Attrs {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { meta } = self;
|
||||||
|
meta.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Meta {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { name, kind } = self;
|
||||||
|
name.weight_of() + kind.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for MetaKind {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
MetaKind::Plain => size_of_val(self),
|
||||||
|
MetaKind::Equals(v) => v.weight_of(),
|
||||||
|
MetaKind::Func(v) => v.weight_of(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Item {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { span, attrs, vis, kind } = self;
|
||||||
|
span.weight_of() + attrs.weight_of() + vis.weight_of() + kind.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for ItemKind {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
ItemKind::Module(v) => v.weight_of(),
|
||||||
|
ItemKind::Alias(v) => v.weight_of(),
|
||||||
|
ItemKind::Enum(v) => v.weight_of(),
|
||||||
|
ItemKind::Struct(v) => v.weight_of(),
|
||||||
|
ItemKind::Const(v) => v.weight_of(),
|
||||||
|
ItemKind::Static(v) => v.weight_of(),
|
||||||
|
ItemKind::Function(v) => v.weight_of(),
|
||||||
|
ItemKind::Impl(v) => v.weight_of(),
|
||||||
|
ItemKind::Use(v) => v.weight_of(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Generics {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { vars } = self;
|
||||||
|
vars.iter().map(|v| v.weight_of()).sum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Module {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { name, file } = self;
|
||||||
|
name.weight_of() + file.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Alias {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { name, from } = self;
|
||||||
|
name.weight_of() + from.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Const {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { name, ty, init } = self;
|
||||||
|
name.weight_of() + ty.weight_of() + init.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Static {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { mutable, name, ty, init } = self;
|
||||||
|
mutable.weight_of() + name.weight_of() + ty.weight_of() + init.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Function {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { name, gens, sign, bind, body } = self;
|
||||||
|
name.weight_of() + gens.weight_of() + sign.weight_of() + bind.weight_of() + body.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Struct {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { name, gens, kind } = self;
|
||||||
|
name.weight_of() + gens.weight_of() + kind.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for StructKind {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
StructKind::Empty => size_of_val(self),
|
||||||
|
StructKind::Tuple(items) => items.weight_of(),
|
||||||
|
StructKind::Struct(sm) => sm.weight_of(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for StructMember {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { vis, name, ty } = self;
|
||||||
|
vis.weight_of() + name.weight_of() + ty.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Enum {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { name, gens, variants } = self;
|
||||||
|
name.weight_of() + gens.weight_of() + variants.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Variant {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { name, kind, body } = self;
|
||||||
|
name.weight_of() + kind.weight_of() + body.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Impl {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { gens, target, body } = self;
|
||||||
|
gens.weight_of() + target.weight_of() + body.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for ImplKind {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
ImplKind::Type(ty) => ty.weight_of(),
|
||||||
|
ImplKind::Trait { impl_trait, for_type } => {
|
||||||
|
impl_trait.weight_of() + for_type.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Use {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { absolute, tree } = self;
|
||||||
|
absolute.weight_of() + tree.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for UseTree {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
UseTree::Tree(tr) => tr.weight_of(),
|
||||||
|
UseTree::Path(pa, tr) => pa.weight_of() + tr.weight_of(),
|
||||||
|
UseTree::Alias(src, dst) => src.weight_of() + dst.weight_of(),
|
||||||
|
UseTree::Name(src) => src.weight_of(),
|
||||||
|
UseTree::Glob => size_of_val(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Ty {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { span, kind, gens } = self;
|
||||||
|
span.weight_of() + kind.weight_of() + gens.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for TyKind {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
TyKind::Never | TyKind::Infer => size_of_val(self),
|
||||||
|
TyKind::Path(v) => v.weight_of(),
|
||||||
|
TyKind::Array(v) => v.weight_of(),
|
||||||
|
TyKind::Slice(v) => v.weight_of(),
|
||||||
|
TyKind::Tuple(v) => v.weight_of(),
|
||||||
|
TyKind::Ref(v) => v.weight_of(),
|
||||||
|
TyKind::Ptr(v) => v.weight_of(),
|
||||||
|
TyKind::Fn(v) => v.weight_of(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for TyArray {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { ty, count } = self;
|
||||||
|
ty.weight_of() + count.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for TySlice {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { ty } = self;
|
||||||
|
ty.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for TyTuple {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { types } = self;
|
||||||
|
types.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for TyRef {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { mutable, count, to } = self;
|
||||||
|
mutable.weight_of() + count.weight_of() + to.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for TyPtr {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { to } = self;
|
||||||
|
to.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for TyFn {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { args, rety } = self;
|
||||||
|
args.weight_of() + rety.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Path {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { absolute, parts } = self;
|
||||||
|
absolute.weight_of() + parts.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for PathPart {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
PathPart::SuperKw => size_of_val(self),
|
||||||
|
PathPart::SelfTy => size_of_val(self),
|
||||||
|
PathPart::Ident(interned) => interned.weight_of(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Stmt {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { span, kind, semi } = self;
|
||||||
|
span.weight_of() + kind.weight_of() + semi.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for StmtKind {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
StmtKind::Empty => size_of_val(self),
|
||||||
|
StmtKind::Item(item) => item.weight_of(),
|
||||||
|
StmtKind::Expr(expr) => expr.weight_of(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Expr {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { span, kind } = self;
|
||||||
|
span.weight_of() + kind.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for ExprKind {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
ExprKind::Empty => size_of_val(self),
|
||||||
|
ExprKind::Closure(v) => v.weight_of(),
|
||||||
|
ExprKind::Quote(v) => v.weight_of(),
|
||||||
|
ExprKind::Let(v) => v.weight_of(),
|
||||||
|
ExprKind::Match(v) => v.weight_of(),
|
||||||
|
ExprKind::Assign(v) => v.weight_of(),
|
||||||
|
ExprKind::Modify(v) => v.weight_of(),
|
||||||
|
ExprKind::Binary(v) => v.weight_of(),
|
||||||
|
ExprKind::Unary(v) => v.weight_of(),
|
||||||
|
ExprKind::Cast(v) => v.weight_of(),
|
||||||
|
ExprKind::Member(v) => v.weight_of(),
|
||||||
|
ExprKind::Index(v) => v.weight_of(),
|
||||||
|
ExprKind::Structor(v) => v.weight_of(),
|
||||||
|
ExprKind::Path(v) => v.weight_of(),
|
||||||
|
ExprKind::Literal(v) => v.weight_of(),
|
||||||
|
ExprKind::Array(v) => v.weight_of(),
|
||||||
|
ExprKind::ArrayRep(v) => v.weight_of(),
|
||||||
|
ExprKind::AddrOf(v) => v.weight_of(),
|
||||||
|
ExprKind::Block(v) => v.weight_of(),
|
||||||
|
ExprKind::Group(v) => v.weight_of(),
|
||||||
|
ExprKind::Tuple(v) => v.weight_of(),
|
||||||
|
ExprKind::While(v) => v.weight_of(),
|
||||||
|
ExprKind::If(v) => v.weight_of(),
|
||||||
|
ExprKind::For(v) => v.weight_of(),
|
||||||
|
ExprKind::Break(v) => v.weight_of(),
|
||||||
|
ExprKind::Return(v) => v.weight_of(),
|
||||||
|
ExprKind::Continue => size_of_val(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Closure {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { arg, body } = self;
|
||||||
|
arg.weight_of() + body.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Quote {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { quote } = self;
|
||||||
|
quote.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Let {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { mutable, name, ty, init } = self;
|
||||||
|
mutable.weight_of() + name.weight_of() + ty.weight_of() + init.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Pattern {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Pattern::Name(s) => size_of_val(s),
|
||||||
|
Pattern::Path(p) => p.weight_of(),
|
||||||
|
Pattern::Literal(literal) => literal.weight_of(),
|
||||||
|
Pattern::Rest(Some(pattern)) => pattern.weight_of(),
|
||||||
|
Pattern::Rest(None) => 0,
|
||||||
|
Pattern::Ref(mutability, pattern) => mutability.weight_of() + pattern.weight_of(),
|
||||||
|
Pattern::RangeExc(head, tail) => head.weight_of() + tail.weight_of(),
|
||||||
|
Pattern::RangeInc(head, tail) => head.weight_of() + tail.weight_of(),
|
||||||
|
Pattern::Tuple(patterns) | Pattern::Array(patterns) => patterns.weight_of(),
|
||||||
|
Pattern::Struct(path, items) => {
|
||||||
|
let sitems: usize = items
|
||||||
|
.iter()
|
||||||
|
.map(|(name, opt)| name.weight_of() + opt.weight_of())
|
||||||
|
.sum();
|
||||||
|
path.weight_of() + sitems
|
||||||
|
}
|
||||||
|
Pattern::TupleStruct(path, patterns) => path.weight_of() + patterns.weight_of(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Match {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { scrutinee, arms } = self;
|
||||||
|
scrutinee.weight_of() + arms.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for MatchArm {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self(pattern, expr) = self;
|
||||||
|
pattern.weight_of() + expr.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Assign {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { parts } = self;
|
||||||
|
|
||||||
|
parts.0.weight_of() + parts.1.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Modify {
|
||||||
|
#[rustfmt::skip]
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { kind, parts } = self;
|
||||||
|
kind.weight_of()
|
||||||
|
+ parts.0.weight_of()
|
||||||
|
+ parts.1.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Binary {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { kind, parts } = self;
|
||||||
|
|
||||||
|
kind.weight_of() + parts.0.weight_of() + parts.1.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Unary {
|
||||||
|
#[rustfmt::skip]
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { kind, tail } = self;
|
||||||
|
kind.weight_of() + tail.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Cast {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { head, ty } = self;
|
||||||
|
head.weight_of() + ty.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Member {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { head, kind } = self;
|
||||||
|
|
||||||
|
head.weight_of() + kind.weight_of() // accounting
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for MemberKind {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
MemberKind::Call(_, tuple) => tuple.weight_of(),
|
||||||
|
MemberKind::Struct(_) => 0,
|
||||||
|
MemberKind::Tuple(literal) => literal.weight_of(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Index {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { head, indices } = self;
|
||||||
|
head.weight_of() + indices.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Literal {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Literal::Bool(v) => v.weight_of(),
|
||||||
|
Literal::Char(v) => v.weight_of(),
|
||||||
|
Literal::Int(v) => v.weight_of(),
|
||||||
|
Literal::Float(v) => v.weight_of(),
|
||||||
|
Literal::String(v) => v.weight_of(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Structor {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { to, init } = self;
|
||||||
|
to.weight_of() + init.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Fielder {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { name, init } = self;
|
||||||
|
name.weight_of() + init.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Array {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { values } = self;
|
||||||
|
values.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for ArrayRep {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { value, repeat } = self;
|
||||||
|
value.weight_of() + repeat.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for AddrOf {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { mutable, expr } = self;
|
||||||
|
mutable.weight_of() + expr.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Block {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { stmts } = self;
|
||||||
|
stmts.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Group {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { expr } = self;
|
||||||
|
expr.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Tuple {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { exprs } = self;
|
||||||
|
exprs.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for While {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { cond, pass, fail } = self;
|
||||||
|
cond.weight_of() + pass.weight_of() + fail.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for If {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { cond, pass, fail } = self;
|
||||||
|
cond.weight_of() + pass.weight_of() + fail.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for For {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { bind, cond, pass, fail } = self;
|
||||||
|
bind.weight_of() + cond.weight_of() + pass.weight_of() + fail.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Else {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { body } = self;
|
||||||
|
body.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Break {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { body } = self;
|
||||||
|
body.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for Return {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
let Self { body } = self;
|
||||||
|
body.weight_of()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------ SizeOf Blanket Implementations
|
||||||
|
|
||||||
|
impl<T: WeightOf> WeightOf for Option<T> {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Some(t) => t.weight_of().max(size_of_val(t)),
|
||||||
|
None => size_of_val(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: WeightOf> WeightOf for [T] {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
self.iter().map(WeightOf::weight_of).sum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: WeightOf> WeightOf for Vec<T> {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
size_of::<Self>() + self.iter().map(WeightOf::weight_of).sum::<usize>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: WeightOf> WeightOf for Box<T> {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
(**self).weight_of() + size_of::<Self>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightOf for str {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
self.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_size_of! {
|
||||||
|
// primitives
|
||||||
|
u8, u16, u32, u64, u128, usize,
|
||||||
|
i8, i16, i32, i64, i128, isize,
|
||||||
|
f32, f64, bool, char,
|
||||||
|
// cl-structures
|
||||||
|
Span,
|
||||||
|
// cl-ast
|
||||||
|
Visibility, Mutability, Semi, ModifyKind, BinaryKind, UnaryKind
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> WeightOf for Interned<'_, T> {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
size_of_val(self) // interned values are opaque to SizeOF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro impl_size_of($($T:ty),*$(,)?) {
|
||||||
|
$(impl WeightOf for $T {
|
||||||
|
fn weight_of(&self) -> usize {
|
||||||
|
::std::mem::size_of_val(self)
|
||||||
|
}
|
||||||
|
})*
|
||||||
|
}
|
||||||
12
compiler/cl-ast/src/ast_visitor.rs
Normal file
12
compiler/cl-ast/src/ast_visitor.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
//! Contains an [immutable visitor](Visit) and an [owned folder](Fold) trait,
|
||||||
|
//! with default implementations across the entire AST
|
||||||
|
|
||||||
|
pub mod fold;
|
||||||
|
|
||||||
|
pub mod visit;
|
||||||
|
pub mod walk;
|
||||||
|
|
||||||
|
pub use fold::Fold;
|
||||||
|
|
||||||
|
pub use visit::Visit;
|
||||||
|
pub use walk::Walk;
|
||||||
595
compiler/cl-ast/src/ast_visitor/fold.rs
Normal file
595
compiler/cl-ast/src/ast_visitor/fold.rs
Normal file
@@ -0,0 +1,595 @@
|
|||||||
|
//! A folder (implementer of the [Fold] trait) maps ASTs to ASTs
|
||||||
|
|
||||||
|
use crate::ast::*;
|
||||||
|
use cl_structures::span::Span;
|
||||||
|
|
||||||
|
/// Deconstructs the entire AST, and reconstructs it from scratch.
|
||||||
|
///
|
||||||
|
/// Each method acts as a customization point.
|
||||||
|
///
|
||||||
|
/// There are a set of default implementations for enums
|
||||||
|
/// under the name [`or_fold_`*](or_fold_expr_kind),
|
||||||
|
/// provided for ease of use.
|
||||||
|
///
|
||||||
|
/// For all other nodes, traversal is *explicit*.
|
||||||
|
pub trait Fold {
|
||||||
|
fn fold_span(&mut self, span: Span) -> Span {
|
||||||
|
span
|
||||||
|
}
|
||||||
|
fn fold_mutability(&mut self, mutability: Mutability) -> Mutability {
|
||||||
|
mutability
|
||||||
|
}
|
||||||
|
fn fold_visibility(&mut self, visibility: Visibility) -> Visibility {
|
||||||
|
visibility
|
||||||
|
}
|
||||||
|
fn fold_sym(&mut self, ident: Sym) -> Sym {
|
||||||
|
ident
|
||||||
|
}
|
||||||
|
fn fold_literal(&mut self, lit: Literal) -> Literal {
|
||||||
|
or_fold_literal(self, lit)
|
||||||
|
}
|
||||||
|
fn fold_bool(&mut self, b: bool) -> bool {
|
||||||
|
b
|
||||||
|
}
|
||||||
|
fn fold_char(&mut self, c: char) -> char {
|
||||||
|
c
|
||||||
|
}
|
||||||
|
fn fold_int(&mut self, i: u128) -> u128 {
|
||||||
|
i
|
||||||
|
}
|
||||||
|
fn fold_smuggled_float(&mut self, f: u64) -> u64 {
|
||||||
|
f
|
||||||
|
}
|
||||||
|
fn fold_string(&mut self, s: String) -> String {
|
||||||
|
s
|
||||||
|
}
|
||||||
|
fn fold_file(&mut self, f: File) -> File {
|
||||||
|
let File { name, items } = f;
|
||||||
|
File { name, items: items.into_iter().map(|i| self.fold_item(i)).collect() }
|
||||||
|
}
|
||||||
|
fn fold_attrs(&mut self, a: Attrs) -> Attrs {
|
||||||
|
let Attrs { meta } = a;
|
||||||
|
Attrs { meta: meta.into_iter().map(|m| self.fold_meta(m)).collect() }
|
||||||
|
}
|
||||||
|
fn fold_meta(&mut self, m: Meta) -> Meta {
|
||||||
|
let Meta { name, kind } = m;
|
||||||
|
Meta { name: self.fold_sym(name), kind: self.fold_meta_kind(kind) }
|
||||||
|
}
|
||||||
|
fn fold_meta_kind(&mut self, kind: MetaKind) -> MetaKind {
|
||||||
|
or_fold_meta_kind(self, kind)
|
||||||
|
}
|
||||||
|
fn fold_item(&mut self, i: Item) -> Item {
|
||||||
|
let Item { span, attrs, vis, kind } = i;
|
||||||
|
Item {
|
||||||
|
span: self.fold_span(span),
|
||||||
|
attrs: self.fold_attrs(attrs),
|
||||||
|
vis: self.fold_visibility(vis),
|
||||||
|
kind: self.fold_item_kind(kind),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn fold_item_kind(&mut self, kind: ItemKind) -> ItemKind {
|
||||||
|
or_fold_item_kind(self, kind)
|
||||||
|
}
|
||||||
|
fn fold_generics(&mut self, gens: Generics) -> Generics {
|
||||||
|
let Generics { vars } = gens;
|
||||||
|
Generics { vars: vars.into_iter().map(|sym| self.fold_sym(sym)).collect() }
|
||||||
|
}
|
||||||
|
fn fold_alias(&mut self, a: Alias) -> Alias {
|
||||||
|
let Alias { name, from } = a;
|
||||||
|
Alias { name: self.fold_sym(name), from: from.map(|from| Box::new(self.fold_ty(*from))) }
|
||||||
|
}
|
||||||
|
fn fold_const(&mut self, c: Const) -> Const {
|
||||||
|
let Const { name, ty, init } = c;
|
||||||
|
Const {
|
||||||
|
name: self.fold_sym(name),
|
||||||
|
ty: Box::new(self.fold_ty(*ty)),
|
||||||
|
init: Box::new(self.fold_expr(*init)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn fold_static(&mut self, s: Static) -> Static {
|
||||||
|
let Static { mutable, name, ty, init } = s;
|
||||||
|
Static {
|
||||||
|
mutable: self.fold_mutability(mutable),
|
||||||
|
name: self.fold_sym(name),
|
||||||
|
ty: Box::new(self.fold_ty(*ty)),
|
||||||
|
init: Box::new(self.fold_expr(*init)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn fold_module(&mut self, m: Module) -> Module {
|
||||||
|
let Module { name, file } = m;
|
||||||
|
Module { name: self.fold_sym(name), file: file.map(|v| self.fold_file(v)) }
|
||||||
|
}
|
||||||
|
fn fold_function(&mut self, f: Function) -> Function {
|
||||||
|
let Function { name, gens, sign, bind, body } = f;
|
||||||
|
Function {
|
||||||
|
name: self.fold_sym(name),
|
||||||
|
gens: self.fold_generics(gens),
|
||||||
|
sign: self.fold_ty_fn(sign),
|
||||||
|
bind: self.fold_pattern(bind),
|
||||||
|
body: body.map(|b| self.fold_expr(b)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn fold_struct(&mut self, s: Struct) -> Struct {
|
||||||
|
let Struct { name, gens, kind } = s;
|
||||||
|
Struct {
|
||||||
|
name: self.fold_sym(name),
|
||||||
|
gens: self.fold_generics(gens),
|
||||||
|
kind: self.fold_struct_kind(kind),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn fold_struct_kind(&mut self, kind: StructKind) -> StructKind {
|
||||||
|
match kind {
|
||||||
|
StructKind::Empty => StructKind::Empty,
|
||||||
|
StructKind::Tuple(tys) => {
|
||||||
|
StructKind::Tuple(tys.into_iter().map(|t| self.fold_ty(t)).collect())
|
||||||
|
}
|
||||||
|
StructKind::Struct(mem) => StructKind::Struct(
|
||||||
|
mem.into_iter()
|
||||||
|
.map(|m| self.fold_struct_member(m))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn fold_struct_member(&mut self, m: StructMember) -> StructMember {
|
||||||
|
let StructMember { vis, name, ty } = m;
|
||||||
|
StructMember {
|
||||||
|
vis: self.fold_visibility(vis),
|
||||||
|
name: self.fold_sym(name),
|
||||||
|
ty: self.fold_ty(ty),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn fold_enum(&mut self, e: Enum) -> Enum {
|
||||||
|
let Enum { name, gens, variants: kind } = e;
|
||||||
|
Enum {
|
||||||
|
name: self.fold_sym(name),
|
||||||
|
gens: self.fold_generics(gens),
|
||||||
|
variants: kind.into_iter().map(|v| self.fold_variant(v)).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn fold_variant(&mut self, v: Variant) -> Variant {
|
||||||
|
let Variant { name, kind, body } = v;
|
||||||
|
|
||||||
|
Variant {
|
||||||
|
name: self.fold_sym(name),
|
||||||
|
kind: self.fold_struct_kind(kind),
|
||||||
|
body: body.map(|e| Box::new(self.fold_expr(*e))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn fold_impl(&mut self, i: Impl) -> Impl {
|
||||||
|
let Impl { gens, target, body } = i;
|
||||||
|
Impl {
|
||||||
|
gens: self.fold_generics(gens),
|
||||||
|
target: self.fold_impl_kind(target),
|
||||||
|
body: self.fold_file(body),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn fold_impl_kind(&mut self, kind: ImplKind) -> ImplKind {
|
||||||
|
or_fold_impl_kind(self, kind)
|
||||||
|
}
|
||||||
|
fn fold_use(&mut self, u: Use) -> Use {
|
||||||
|
let Use { absolute, tree } = u;
|
||||||
|
Use { absolute, tree: self.fold_use_tree(tree) }
|
||||||
|
}
|
||||||
|
fn fold_use_tree(&mut self, tree: UseTree) -> UseTree {
|
||||||
|
or_fold_use_tree(self, tree)
|
||||||
|
}
|
||||||
|
fn fold_ty(&mut self, t: Ty) -> Ty {
|
||||||
|
let Ty { span, kind, gens } = t;
|
||||||
|
Ty {
|
||||||
|
span: self.fold_span(span),
|
||||||
|
kind: self.fold_ty_kind(kind),
|
||||||
|
gens: self.fold_generics(gens),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn fold_ty_kind(&mut self, kind: TyKind) -> TyKind {
|
||||||
|
or_fold_ty_kind(self, kind)
|
||||||
|
}
|
||||||
|
fn fold_ty_array(&mut self, a: TyArray) -> TyArray {
|
||||||
|
let TyArray { ty, count } = a;
|
||||||
|
TyArray { ty: Box::new(self.fold_ty(*ty)), count }
|
||||||
|
}
|
||||||
|
fn fold_ty_slice(&mut self, s: TySlice) -> TySlice {
|
||||||
|
let TySlice { ty } = s;
|
||||||
|
TySlice { ty: Box::new(self.fold_ty(*ty)) }
|
||||||
|
}
|
||||||
|
fn fold_ty_tuple(&mut self, t: TyTuple) -> TyTuple {
|
||||||
|
let TyTuple { types } = t;
|
||||||
|
TyTuple { types: types.into_iter().map(|kind| self.fold_ty(kind)).collect() }
|
||||||
|
}
|
||||||
|
fn fold_ty_ref(&mut self, t: TyRef) -> TyRef {
|
||||||
|
let TyRef { mutable, count, to } = t;
|
||||||
|
TyRef { mutable: self.fold_mutability(mutable), count, to: Box::new(self.fold_ty(*to)) }
|
||||||
|
}
|
||||||
|
fn fold_ty_ptr(&mut self, t: TyPtr) -> TyPtr {
|
||||||
|
let TyPtr { to } = t;
|
||||||
|
TyPtr { to: Box::new(self.fold_ty(*to)) }
|
||||||
|
}
|
||||||
|
fn fold_ty_fn(&mut self, t: TyFn) -> TyFn {
|
||||||
|
let TyFn { args, rety } = t;
|
||||||
|
TyFn { args: Box::new(self.fold_ty(*args)), rety: Box::new(self.fold_ty(*rety)) }
|
||||||
|
}
|
||||||
|
fn fold_path(&mut self, p: Path) -> Path {
|
||||||
|
let Path { absolute, parts } = p;
|
||||||
|
Path { absolute, parts: parts.into_iter().map(|p| self.fold_path_part(p)).collect() }
|
||||||
|
}
|
||||||
|
fn fold_path_part(&mut self, p: PathPart) -> PathPart {
|
||||||
|
match p {
|
||||||
|
PathPart::SuperKw => PathPart::SuperKw,
|
||||||
|
PathPart::SelfTy => PathPart::SelfTy,
|
||||||
|
PathPart::Ident(i) => PathPart::Ident(self.fold_sym(i)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn fold_stmt(&mut self, s: Stmt) -> Stmt {
|
||||||
|
let Stmt { span, kind, semi } = s;
|
||||||
|
Stmt {
|
||||||
|
span: self.fold_span(span),
|
||||||
|
kind: self.fold_stmt_kind(kind),
|
||||||
|
semi: self.fold_semi(semi),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn fold_stmt_kind(&mut self, kind: StmtKind) -> StmtKind {
|
||||||
|
or_fold_stmt_kind(self, kind)
|
||||||
|
}
|
||||||
|
fn fold_semi(&mut self, s: Semi) -> Semi {
|
||||||
|
s
|
||||||
|
}
|
||||||
|
fn fold_expr(&mut self, e: Expr) -> Expr {
|
||||||
|
let Expr { span, kind } = e;
|
||||||
|
Expr { span: self.fold_span(span), kind: self.fold_expr_kind(kind) }
|
||||||
|
}
|
||||||
|
fn fold_expr_kind(&mut self, kind: ExprKind) -> ExprKind {
|
||||||
|
or_fold_expr_kind(self, kind)
|
||||||
|
}
|
||||||
|
fn fold_closure(&mut self, value: Closure) -> Closure {
|
||||||
|
let Closure { arg, body } = value;
|
||||||
|
Closure { arg: Box::new(self.fold_pattern(*arg)), body: Box::new(self.fold_expr(*body)) }
|
||||||
|
}
|
||||||
|
fn fold_let(&mut self, l: Let) -> Let {
|
||||||
|
let Let { mutable, name, ty, init } = l;
|
||||||
|
Let {
|
||||||
|
mutable: self.fold_mutability(mutable),
|
||||||
|
name: self.fold_pattern(name),
|
||||||
|
ty: ty.map(|t| Box::new(self.fold_ty(*t))),
|
||||||
|
init: init.map(|e| Box::new(self.fold_expr(*e))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_pattern(&mut self, p: Pattern) -> Pattern {
|
||||||
|
match p {
|
||||||
|
Pattern::Name(sym) => Pattern::Name(self.fold_sym(sym)),
|
||||||
|
Pattern::Path(path) => Pattern::Path(self.fold_path(path)),
|
||||||
|
Pattern::Literal(literal) => Pattern::Literal(self.fold_literal(literal)),
|
||||||
|
Pattern::Rest(Some(name)) => Pattern::Rest(Some(self.fold_pattern(*name).into())),
|
||||||
|
Pattern::Rest(None) => Pattern::Rest(None),
|
||||||
|
Pattern::Ref(mutability, pattern) => Pattern::Ref(
|
||||||
|
self.fold_mutability(mutability),
|
||||||
|
Box::new(self.fold_pattern(*pattern)),
|
||||||
|
),
|
||||||
|
Pattern::RangeExc(head, tail) => Pattern::RangeInc(
|
||||||
|
Box::new(self.fold_pattern(*head)),
|
||||||
|
Box::new(self.fold_pattern(*tail)),
|
||||||
|
),
|
||||||
|
Pattern::RangeInc(head, tail) => Pattern::RangeInc(
|
||||||
|
Box::new(self.fold_pattern(*head)),
|
||||||
|
Box::new(self.fold_pattern(*tail)),
|
||||||
|
),
|
||||||
|
Pattern::Tuple(patterns) => {
|
||||||
|
Pattern::Tuple(patterns.into_iter().map(|p| self.fold_pattern(p)).collect())
|
||||||
|
}
|
||||||
|
Pattern::Array(patterns) => {
|
||||||
|
Pattern::Array(patterns.into_iter().map(|p| self.fold_pattern(p)).collect())
|
||||||
|
}
|
||||||
|
Pattern::Struct(path, items) => Pattern::Struct(
|
||||||
|
self.fold_path(path),
|
||||||
|
items
|
||||||
|
.into_iter()
|
||||||
|
.map(|(name, bind)| (name, bind.map(|p| self.fold_pattern(p))))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
Pattern::TupleStruct(path, items) => Pattern::TupleStruct(
|
||||||
|
self.fold_path(path),
|
||||||
|
items
|
||||||
|
.into_iter()
|
||||||
|
.map(|bind| self.fold_pattern(bind))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_match(&mut self, m: Match) -> Match {
|
||||||
|
let Match { scrutinee, arms } = m;
|
||||||
|
Match {
|
||||||
|
scrutinee: self.fold_expr(*scrutinee).into(),
|
||||||
|
arms: arms
|
||||||
|
.into_iter()
|
||||||
|
.map(|arm| self.fold_match_arm(arm))
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_match_arm(&mut self, a: MatchArm) -> MatchArm {
|
||||||
|
let MatchArm(pat, expr) = a;
|
||||||
|
MatchArm(self.fold_pattern(pat), self.fold_expr(expr))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_assign(&mut self, a: Assign) -> Assign {
|
||||||
|
let Assign { parts } = a;
|
||||||
|
let (head, tail) = *parts;
|
||||||
|
Assign { parts: Box::new((self.fold_expr(head), self.fold_expr(tail))) }
|
||||||
|
}
|
||||||
|
fn fold_modify(&mut self, m: Modify) -> Modify {
|
||||||
|
let Modify { kind, parts } = m;
|
||||||
|
let (head, tail) = *parts;
|
||||||
|
Modify {
|
||||||
|
kind: self.fold_modify_kind(kind),
|
||||||
|
parts: Box::new((self.fold_expr(head), self.fold_expr(tail))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn fold_modify_kind(&mut self, kind: ModifyKind) -> ModifyKind {
|
||||||
|
kind
|
||||||
|
}
|
||||||
|
fn fold_binary(&mut self, b: Binary) -> Binary {
|
||||||
|
let Binary { kind, parts } = b;
|
||||||
|
let (head, tail) = *parts;
|
||||||
|
Binary {
|
||||||
|
kind: self.fold_binary_kind(kind),
|
||||||
|
parts: Box::new((self.fold_expr(head), self.fold_expr(tail))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn fold_binary_kind(&mut self, kind: BinaryKind) -> BinaryKind {
|
||||||
|
kind
|
||||||
|
}
|
||||||
|
fn fold_unary(&mut self, u: Unary) -> Unary {
|
||||||
|
let Unary { kind, tail } = u;
|
||||||
|
Unary { kind: self.fold_unary_kind(kind), tail: Box::new(self.fold_expr(*tail)) }
|
||||||
|
}
|
||||||
|
fn fold_unary_kind(&mut self, kind: UnaryKind) -> UnaryKind {
|
||||||
|
kind
|
||||||
|
}
|
||||||
|
fn fold_cast(&mut self, cast: Cast) -> Cast {
|
||||||
|
let Cast { head, ty } = cast;
|
||||||
|
Cast { head: Box::new(self.fold_expr(*head)), ty: self.fold_ty(ty) }
|
||||||
|
}
|
||||||
|
fn fold_member(&mut self, m: Member) -> Member {
|
||||||
|
let Member { head, kind } = m;
|
||||||
|
Member { head: Box::new(self.fold_expr(*head)), kind: self.fold_member_kind(kind) }
|
||||||
|
}
|
||||||
|
fn fold_member_kind(&mut self, kind: MemberKind) -> MemberKind {
|
||||||
|
or_fold_member_kind(self, kind)
|
||||||
|
}
|
||||||
|
fn fold_index(&mut self, i: Index) -> Index {
|
||||||
|
let Index { head, indices } = i;
|
||||||
|
Index {
|
||||||
|
head: Box::new(self.fold_expr(*head)),
|
||||||
|
indices: indices.into_iter().map(|e| self.fold_expr(e)).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_structor(&mut self, s: Structor) -> Structor {
|
||||||
|
let Structor { to, init } = s;
|
||||||
|
Structor {
|
||||||
|
to: self.fold_path(to),
|
||||||
|
init: init.into_iter().map(|f| self.fold_fielder(f)).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_fielder(&mut self, f: Fielder) -> Fielder {
|
||||||
|
let Fielder { name, init } = f;
|
||||||
|
Fielder { name: self.fold_sym(name), init: init.map(|e| Box::new(self.fold_expr(*e))) }
|
||||||
|
}
|
||||||
|
fn fold_array(&mut self, a: Array) -> Array {
|
||||||
|
let Array { values } = a;
|
||||||
|
Array { values: values.into_iter().map(|e| self.fold_expr(e)).collect() }
|
||||||
|
}
|
||||||
|
fn fold_array_rep(&mut self, a: ArrayRep) -> ArrayRep {
|
||||||
|
let ArrayRep { value, repeat } = a;
|
||||||
|
ArrayRep { value: Box::new(self.fold_expr(*value)), repeat }
|
||||||
|
}
|
||||||
|
fn fold_addrof(&mut self, a: AddrOf) -> AddrOf {
|
||||||
|
let AddrOf { mutable, expr } = a;
|
||||||
|
AddrOf { mutable: self.fold_mutability(mutable), expr: Box::new(self.fold_expr(*expr)) }
|
||||||
|
}
|
||||||
|
fn fold_block(&mut self, b: Block) -> Block {
|
||||||
|
let Block { stmts } = b;
|
||||||
|
Block { stmts: stmts.into_iter().map(|s| self.fold_stmt(s)).collect() }
|
||||||
|
}
|
||||||
|
fn fold_group(&mut self, g: Group) -> Group {
|
||||||
|
let Group { expr } = g;
|
||||||
|
Group { expr: Box::new(self.fold_expr(*expr)) }
|
||||||
|
}
|
||||||
|
fn fold_tuple(&mut self, t: Tuple) -> Tuple {
|
||||||
|
let Tuple { exprs } = t;
|
||||||
|
Tuple { exprs: exprs.into_iter().map(|e| self.fold_expr(e)).collect() }
|
||||||
|
}
|
||||||
|
fn fold_while(&mut self, w: While) -> While {
|
||||||
|
let While { cond, pass, fail } = w;
|
||||||
|
While {
|
||||||
|
cond: Box::new(self.fold_expr(*cond)),
|
||||||
|
pass: Box::new(self.fold_block(*pass)),
|
||||||
|
fail: self.fold_else(fail),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn fold_if(&mut self, i: If) -> If {
|
||||||
|
let If { cond, pass, fail } = i;
|
||||||
|
If {
|
||||||
|
cond: Box::new(self.fold_expr(*cond)),
|
||||||
|
pass: Box::new(self.fold_block(*pass)),
|
||||||
|
fail: self.fold_else(fail),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn fold_for(&mut self, f: For) -> For {
|
||||||
|
let For { bind, cond, pass, fail } = f;
|
||||||
|
For {
|
||||||
|
bind: self.fold_pattern(bind),
|
||||||
|
cond: Box::new(self.fold_expr(*cond)),
|
||||||
|
pass: Box::new(self.fold_block(*pass)),
|
||||||
|
fail: self.fold_else(fail),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn fold_else(&mut self, e: Else) -> Else {
|
||||||
|
let Else { body } = e;
|
||||||
|
Else { body: body.map(|e| Box::new(self.fold_expr(*e))) }
|
||||||
|
}
|
||||||
|
fn fold_break(&mut self, b: Break) -> Break {
|
||||||
|
let Break { body } = b;
|
||||||
|
Break { body: body.map(|e| Box::new(self.fold_expr(*e))) }
|
||||||
|
}
|
||||||
|
fn fold_return(&mut self, r: Return) -> Return {
|
||||||
|
let Return { body } = r;
|
||||||
|
Return { body: body.map(|e| Box::new(self.fold_expr(*e))) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Folds a [Literal] in the default way
|
||||||
|
pub fn or_fold_literal<F: Fold + ?Sized>(folder: &mut F, lit: Literal) -> Literal {
|
||||||
|
match lit {
|
||||||
|
Literal::Bool(b) => Literal::Bool(folder.fold_bool(b)),
|
||||||
|
Literal::Char(c) => Literal::Char(folder.fold_char(c)),
|
||||||
|
Literal::Int(i) => Literal::Int(folder.fold_int(i)),
|
||||||
|
Literal::Float(f) => Literal::Float(folder.fold_smuggled_float(f)),
|
||||||
|
Literal::String(s) => Literal::String(folder.fold_string(s)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Folds a [MetaKind] in the default way
|
||||||
|
pub fn or_fold_meta_kind<F: Fold + ?Sized>(folder: &mut F, kind: MetaKind) -> MetaKind {
|
||||||
|
match kind {
|
||||||
|
MetaKind::Plain => MetaKind::Plain,
|
||||||
|
MetaKind::Equals(l) => MetaKind::Equals(folder.fold_literal(l)),
|
||||||
|
MetaKind::Func(lits) => {
|
||||||
|
MetaKind::Func(lits.into_iter().map(|l| folder.fold_literal(l)).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Folds an [ItemKind] in the default way
|
||||||
|
pub fn or_fold_item_kind<F: Fold + ?Sized>(folder: &mut F, kind: ItemKind) -> ItemKind {
|
||||||
|
match kind {
|
||||||
|
ItemKind::Module(m) => ItemKind::Module(folder.fold_module(m)),
|
||||||
|
ItemKind::Alias(a) => ItemKind::Alias(folder.fold_alias(a)),
|
||||||
|
ItemKind::Enum(e) => ItemKind::Enum(folder.fold_enum(e)),
|
||||||
|
ItemKind::Struct(s) => ItemKind::Struct(folder.fold_struct(s)),
|
||||||
|
ItemKind::Const(c) => ItemKind::Const(folder.fold_const(c)),
|
||||||
|
ItemKind::Static(s) => ItemKind::Static(folder.fold_static(s)),
|
||||||
|
ItemKind::Function(f) => ItemKind::Function(folder.fold_function(f)),
|
||||||
|
ItemKind::Impl(i) => ItemKind::Impl(folder.fold_impl(i)),
|
||||||
|
ItemKind::Use(u) => ItemKind::Use(folder.fold_use(u)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Folds a [StructKind] in the default way
|
||||||
|
pub fn or_fold_struct_kind<F: Fold + ?Sized>(folder: &mut F, kind: StructKind) -> StructKind {
|
||||||
|
match kind {
|
||||||
|
StructKind::Empty => StructKind::Empty,
|
||||||
|
StructKind::Tuple(tys) => {
|
||||||
|
StructKind::Tuple(tys.into_iter().map(|t| folder.fold_ty(t)).collect())
|
||||||
|
}
|
||||||
|
StructKind::Struct(mem) => StructKind::Struct(
|
||||||
|
mem.into_iter()
|
||||||
|
.map(|m| folder.fold_struct_member(m))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Folds an [ImplKind] in the default way
|
||||||
|
pub fn or_fold_impl_kind<F: Fold + ?Sized>(folder: &mut F, kind: ImplKind) -> ImplKind {
|
||||||
|
match kind {
|
||||||
|
ImplKind::Type(t) => ImplKind::Type(folder.fold_ty(t)),
|
||||||
|
ImplKind::Trait { impl_trait, for_type } => ImplKind::Trait {
|
||||||
|
impl_trait: folder.fold_path(impl_trait),
|
||||||
|
for_type: Box::new(folder.fold_ty(*for_type)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn or_fold_use_tree<F: Fold + ?Sized>(folder: &mut F, tree: UseTree) -> UseTree {
|
||||||
|
match tree {
|
||||||
|
UseTree::Tree(tree) => UseTree::Tree(
|
||||||
|
tree.into_iter()
|
||||||
|
.map(|tree| folder.fold_use_tree(tree))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
UseTree::Path(path, rest) => UseTree::Path(
|
||||||
|
folder.fold_path_part(path),
|
||||||
|
Box::new(folder.fold_use_tree(*rest)),
|
||||||
|
),
|
||||||
|
UseTree::Alias(path, name) => UseTree::Alias(folder.fold_sym(path), folder.fold_sym(name)),
|
||||||
|
UseTree::Name(name) => UseTree::Name(folder.fold_sym(name)),
|
||||||
|
UseTree::Glob => UseTree::Glob,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Folds a [TyKind] in the default way
|
||||||
|
pub fn or_fold_ty_kind<F: Fold + ?Sized>(folder: &mut F, kind: TyKind) -> TyKind {
|
||||||
|
match kind {
|
||||||
|
TyKind::Never => TyKind::Never,
|
||||||
|
TyKind::Infer => TyKind::Infer,
|
||||||
|
TyKind::Path(p) => TyKind::Path(folder.fold_path(p)),
|
||||||
|
TyKind::Array(a) => TyKind::Array(folder.fold_ty_array(a)),
|
||||||
|
TyKind::Slice(s) => TyKind::Slice(folder.fold_ty_slice(s)),
|
||||||
|
TyKind::Tuple(t) => TyKind::Tuple(folder.fold_ty_tuple(t)),
|
||||||
|
TyKind::Ref(t) => TyKind::Ref(folder.fold_ty_ref(t)),
|
||||||
|
TyKind::Ptr(t) => TyKind::Ptr(folder.fold_ty_ptr(t)),
|
||||||
|
TyKind::Fn(t) => TyKind::Fn(folder.fold_ty_fn(t)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Folds a [StmtKind] in the default way
|
||||||
|
pub fn or_fold_stmt_kind<F: Fold + ?Sized>(folder: &mut F, kind: StmtKind) -> StmtKind {
|
||||||
|
match kind {
|
||||||
|
StmtKind::Empty => StmtKind::Empty,
|
||||||
|
StmtKind::Item(i) => StmtKind::Item(Box::new(folder.fold_item(*i))),
|
||||||
|
StmtKind::Expr(e) => StmtKind::Expr(Box::new(folder.fold_expr(*e))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
/// Folds an [ExprKind] in the default way
|
||||||
|
pub fn or_fold_expr_kind<F: Fold + ?Sized>(folder: &mut F, kind: ExprKind) -> ExprKind {
|
||||||
|
match kind {
|
||||||
|
ExprKind::Empty => ExprKind::Empty,
|
||||||
|
ExprKind::Closure(c) => ExprKind::Closure(folder.fold_closure(c)),
|
||||||
|
ExprKind::Quote(q) => ExprKind::Quote(q), // quoted expressions are left unmodified
|
||||||
|
ExprKind::Let(l) => ExprKind::Let(folder.fold_let(l)),
|
||||||
|
ExprKind::Match(m) => ExprKind::Match(folder.fold_match(m)),
|
||||||
|
ExprKind::Assign(a) => ExprKind::Assign(folder.fold_assign(a)),
|
||||||
|
ExprKind::Modify(m) => ExprKind::Modify(folder.fold_modify(m)),
|
||||||
|
ExprKind::Binary(b) => ExprKind::Binary(folder.fold_binary(b)),
|
||||||
|
ExprKind::Unary(u) => ExprKind::Unary(folder.fold_unary(u)),
|
||||||
|
ExprKind::Cast(c) => ExprKind::Cast(folder.fold_cast(c)),
|
||||||
|
ExprKind::Member(m) => ExprKind::Member(folder.fold_member(m)),
|
||||||
|
ExprKind::Index(i) => ExprKind::Index(folder.fold_index(i)),
|
||||||
|
ExprKind::Structor(s) => ExprKind::Structor(folder.fold_structor(s)),
|
||||||
|
ExprKind::Path(p) => ExprKind::Path(folder.fold_path(p)),
|
||||||
|
ExprKind::Literal(l) => ExprKind::Literal(folder.fold_literal(l)),
|
||||||
|
ExprKind::Array(a) => ExprKind::Array(folder.fold_array(a)),
|
||||||
|
ExprKind::ArrayRep(a) => ExprKind::ArrayRep(folder.fold_array_rep(a)),
|
||||||
|
ExprKind::AddrOf(a) => ExprKind::AddrOf(folder.fold_addrof(a)),
|
||||||
|
ExprKind::Block(b) => ExprKind::Block(folder.fold_block(b)),
|
||||||
|
ExprKind::Group(g) => ExprKind::Group(folder.fold_group(g)),
|
||||||
|
ExprKind::Tuple(t) => ExprKind::Tuple(folder.fold_tuple(t)),
|
||||||
|
ExprKind::While(w) => ExprKind::While(folder.fold_while(w)),
|
||||||
|
ExprKind::If(i) => ExprKind::If(folder.fold_if(i)),
|
||||||
|
ExprKind::For(f) => ExprKind::For(folder.fold_for(f)),
|
||||||
|
ExprKind::Break(b) => ExprKind::Break(folder.fold_break(b)),
|
||||||
|
ExprKind::Return(r) => ExprKind::Return(folder.fold_return(r)),
|
||||||
|
ExprKind::Continue => ExprKind::Continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn or_fold_member_kind<F: Fold + ?Sized>(folder: &mut F, kind: MemberKind) -> MemberKind {
|
||||||
|
match kind {
|
||||||
|
MemberKind::Call(name, args) => {
|
||||||
|
MemberKind::Call(folder.fold_sym(name), folder.fold_tuple(args))
|
||||||
|
}
|
||||||
|
MemberKind::Struct(name) => MemberKind::Struct(folder.fold_sym(name)),
|
||||||
|
MemberKind::Tuple(name) => MemberKind::Tuple(folder.fold_literal(name)),
|
||||||
|
}
|
||||||
|
}
|
||||||
260
compiler/cl-ast/src/ast_visitor/visit.rs
Normal file
260
compiler/cl-ast/src/ast_visitor/visit.rs
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
//! A [visitor](Visit) (implementer of the [Visit] trait) walks the immutable AST, mutating itself.
|
||||||
|
|
||||||
|
use crate::ast::*;
|
||||||
|
use cl_structures::span::Span;
|
||||||
|
|
||||||
|
use super::walk::Walk;
|
||||||
|
|
||||||
|
/// Immutably walks the entire AST
|
||||||
|
///
|
||||||
|
/// Each method acts as a customization point.
|
||||||
|
pub trait Visit<'a>: Sized {
|
||||||
|
/// Visits a [Walker](Walk)
|
||||||
|
#[inline]
|
||||||
|
fn visit<W: Walk>(&mut self, walker: &'a W) -> &mut Self {
|
||||||
|
walker.visit_in(self);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Visits the children of a [Walker](Walk)
|
||||||
|
fn visit_children<W: Walk>(&mut self, walker: &'a W) {
|
||||||
|
walker.children(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_span(&mut self, value: &'a Span) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_mutability(&mut self, value: &'a Mutability) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_visibility(&mut self, value: &'a Visibility) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_sym(&mut self, value: &'a Sym) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_literal(&mut self, value: &'a Literal) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_bool(&mut self, value: &'a bool) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_char(&mut self, value: &'a char) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_int(&mut self, value: &'a u128) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_smuggled_float(&mut self, value: &'a u64) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_string(&mut self, value: &'a str) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_file(&mut self, value: &'a File) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_attrs(&mut self, value: &'a Attrs) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_meta(&mut self, value: &'a Meta) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_meta_kind(&mut self, value: &'a MetaKind) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_item(&mut self, value: &'a Item) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_item_kind(&mut self, value: &'a ItemKind) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_generics(&mut self, value: &'a Generics) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_alias(&mut self, value: &'a Alias) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_const(&mut self, value: &'a Const) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_static(&mut self, value: &'a Static) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_module(&mut self, value: &'a Module) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_function(&mut self, value: &'a Function) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_struct(&mut self, value: &'a Struct) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_struct_kind(&mut self, value: &'a StructKind) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_struct_member(&mut self, value: &'a StructMember) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_enum(&mut self, value: &'a Enum) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_variant(&mut self, value: &'a Variant) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_impl(&mut self, value: &'a Impl) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_impl_kind(&mut self, value: &'a ImplKind) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_use(&mut self, value: &'a Use) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_use_tree(&mut self, value: &'a UseTree) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_ty(&mut self, value: &'a Ty) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_ty_kind(&mut self, value: &'a TyKind) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_ty_array(&mut self, value: &'a TyArray) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_ty_slice(&mut self, value: &'a TySlice) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_ty_tuple(&mut self, value: &'a TyTuple) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_ty_ref(&mut self, value: &'a TyRef) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_ty_ptr(&mut self, value: &'a TyPtr) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_ty_fn(&mut self, value: &'a TyFn) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_path(&mut self, value: &'a Path) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_path_part(&mut self, value: &'a PathPart) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_stmt(&mut self, value: &'a Stmt) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_stmt_kind(&mut self, value: &'a StmtKind) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_semi(&mut self, value: &'a Semi) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_expr(&mut self, value: &'a Expr) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_expr_kind(&mut self, value: &'a ExprKind) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_closure(&mut self, value: &'a Closure) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_quote(&mut self, value: &'a Quote) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_let(&mut self, value: &'a Let) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_pattern(&mut self, value: &'a Pattern) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_match(&mut self, value: &'a Match) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_match_arm(&mut self, value: &'a MatchArm) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_assign(&mut self, value: &'a Assign) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_modify(&mut self, value: &'a Modify) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_modify_kind(&mut self, value: &'a ModifyKind) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_binary(&mut self, value: &'a Binary) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_binary_kind(&mut self, value: &'a BinaryKind) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_unary(&mut self, value: &'a Unary) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_unary_kind(&mut self, value: &'a UnaryKind) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_cast(&mut self, value: &'a Cast) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_member(&mut self, value: &'a Member) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_member_kind(&mut self, value: &'a MemberKind) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_index(&mut self, value: &'a Index) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_structor(&mut self, value: &'a Structor) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_fielder(&mut self, value: &'a Fielder) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_array(&mut self, value: &'a Array) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_array_rep(&mut self, value: &'a ArrayRep) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_addrof(&mut self, value: &'a AddrOf) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_block(&mut self, value: &'a Block) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_group(&mut self, value: &'a Group) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_tuple(&mut self, value: &'a Tuple) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_while(&mut self, value: &'a While) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_if(&mut self, value: &'a If) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_for(&mut self, value: &'a For) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_else(&mut self, value: &'a Else) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_break(&mut self, value: &'a Break) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_return(&mut self, value: &'a Return) {
|
||||||
|
value.children(self)
|
||||||
|
}
|
||||||
|
fn visit_continue(&mut self) {}
|
||||||
|
}
|
||||||
963
compiler/cl-ast/src/ast_visitor/walk.rs
Normal file
963
compiler/cl-ast/src/ast_visitor/walk.rs
Normal file
@@ -0,0 +1,963 @@
|
|||||||
|
//! Accepts an AST Visitor. Walks the AST, calling the visitor on each step.
|
||||||
|
|
||||||
|
use super::visit::Visit;
|
||||||
|
use crate::ast::*;
|
||||||
|
use cl_structures::span::Span;
|
||||||
|
|
||||||
|
/// Helps a [Visitor](Visit) walk through `Self`.
|
||||||
|
pub trait Walk {
|
||||||
|
/// Calls the respective `visit_*` function in V
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V);
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
/// Walks the children of self, visiting them in V
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Walk for Span {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_span(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Sym {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_sym(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Mutability {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_mutability(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Visibility {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_visibility(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for bool {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_bool(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for char {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_char(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for u128 {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_int(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for u64 {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_smuggled_float(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for str {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_string(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Literal {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_literal(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
match self {
|
||||||
|
Literal::Bool(value) => value.children(v),
|
||||||
|
Literal::Char(value) => value.children(v),
|
||||||
|
Literal::Int(value) => value.children(v),
|
||||||
|
Literal::Float(value) => value.children(v),
|
||||||
|
Literal::String(value) => value.children(v),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for File {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_file(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let File { name: _, items } = self;
|
||||||
|
items.iter().for_each(|i| v.visit_item(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Attrs {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_attrs(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Attrs { meta } = self;
|
||||||
|
meta.children(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Meta {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_meta(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Meta { name, kind } = self;
|
||||||
|
name.visit_in(v);
|
||||||
|
kind.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for MetaKind {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_meta_kind(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
match self {
|
||||||
|
MetaKind::Plain => {}
|
||||||
|
MetaKind::Equals(lit) => lit.visit_in(v),
|
||||||
|
MetaKind::Func(lits) => lits.visit_in(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Item {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_item(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Item { span, attrs, vis, kind } = self;
|
||||||
|
span.visit_in(v);
|
||||||
|
attrs.visit_in(v);
|
||||||
|
vis.visit_in(v);
|
||||||
|
kind.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for ItemKind {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_item_kind(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
match self {
|
||||||
|
ItemKind::Module(value) => value.visit_in(v),
|
||||||
|
ItemKind::Alias(value) => value.visit_in(v),
|
||||||
|
ItemKind::Enum(value) => value.visit_in(v),
|
||||||
|
ItemKind::Struct(value) => value.visit_in(v),
|
||||||
|
ItemKind::Const(value) => value.visit_in(v),
|
||||||
|
ItemKind::Static(value) => value.visit_in(v),
|
||||||
|
ItemKind::Function(value) => value.visit_in(v),
|
||||||
|
ItemKind::Impl(value) => value.visit_in(v),
|
||||||
|
ItemKind::Use(value) => value.visit_in(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Generics {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_generics(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Self { vars } = self;
|
||||||
|
vars.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Module {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_module(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Module { name, file } = self;
|
||||||
|
name.visit_in(v);
|
||||||
|
file.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Alias {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_alias(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Alias { name, from } = self;
|
||||||
|
name.visit_in(v);
|
||||||
|
from.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Const {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_const(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Const { name, ty, init } = self;
|
||||||
|
name.visit_in(v);
|
||||||
|
ty.visit_in(v);
|
||||||
|
init.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Static {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_static(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Static { mutable, name, ty, init } = self;
|
||||||
|
mutable.visit_in(v);
|
||||||
|
name.visit_in(v);
|
||||||
|
ty.visit_in(v);
|
||||||
|
init.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Function {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_function(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Function { name, gens, sign, bind, body } = self;
|
||||||
|
name.visit_in(v);
|
||||||
|
gens.visit_in(v);
|
||||||
|
sign.visit_in(v);
|
||||||
|
bind.visit_in(v);
|
||||||
|
body.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Struct {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_struct(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Struct { name, gens, kind } = self;
|
||||||
|
name.visit_in(v);
|
||||||
|
gens.visit_in(v);
|
||||||
|
kind.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for StructKind {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_struct_kind(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
match self {
|
||||||
|
StructKind::Empty => {}
|
||||||
|
StructKind::Tuple(tys) => tys.visit_in(v),
|
||||||
|
StructKind::Struct(ms) => ms.visit_in(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for StructMember {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_struct_member(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let StructMember { vis, name, ty } = self;
|
||||||
|
vis.visit_in(v);
|
||||||
|
name.visit_in(v);
|
||||||
|
ty.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Enum {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_enum(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Enum { name, gens, variants } = self;
|
||||||
|
name.visit_in(v);
|
||||||
|
gens.visit_in(v);
|
||||||
|
variants.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Variant {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_variant(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Variant { name, kind, body } = self;
|
||||||
|
name.visit_in(v);
|
||||||
|
kind.visit_in(v);
|
||||||
|
body.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Impl {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_impl(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Impl { gens, target, body } = self;
|
||||||
|
gens.visit_in(v);
|
||||||
|
target.visit_in(v);
|
||||||
|
body.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for ImplKind {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_impl_kind(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
match self {
|
||||||
|
ImplKind::Type(t) => t.visit_in(v),
|
||||||
|
ImplKind::Trait { impl_trait, for_type } => {
|
||||||
|
impl_trait.visit_in(v);
|
||||||
|
for_type.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Use {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_use(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Use { absolute: _, tree } = self;
|
||||||
|
tree.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for UseTree {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_use_tree(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
match self {
|
||||||
|
UseTree::Tree(tree) => tree.iter().for_each(|t| t.visit_in(v)),
|
||||||
|
UseTree::Path(part, tree) => {
|
||||||
|
part.visit_in(v);
|
||||||
|
tree.visit_in(v);
|
||||||
|
}
|
||||||
|
UseTree::Alias(from, to) => {
|
||||||
|
from.visit_in(v);
|
||||||
|
to.visit_in(v);
|
||||||
|
}
|
||||||
|
UseTree::Name(name) => name.visit_in(v),
|
||||||
|
UseTree::Glob => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Ty {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_ty(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Ty { span, kind, gens } = self;
|
||||||
|
span.visit_in(v);
|
||||||
|
kind.visit_in(v);
|
||||||
|
gens.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for TyKind {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_ty_kind(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
match self {
|
||||||
|
TyKind::Never => {}
|
||||||
|
TyKind::Infer => {}
|
||||||
|
TyKind::Path(value) => value.visit_in(v),
|
||||||
|
TyKind::Array(value) => value.visit_in(v),
|
||||||
|
TyKind::Slice(value) => value.visit_in(v),
|
||||||
|
TyKind::Tuple(value) => value.visit_in(v),
|
||||||
|
TyKind::Ref(value) => value.visit_in(v),
|
||||||
|
TyKind::Ptr(value) => value.visit_in(v),
|
||||||
|
TyKind::Fn(value) => value.visit_in(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for TyArray {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_ty_array(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let TyArray { ty, count: _ } = self;
|
||||||
|
ty.visit_in(v);
|
||||||
|
// count.walk(v); // not available
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for TySlice {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_ty_slice(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let TySlice { ty } = self;
|
||||||
|
ty.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for TyTuple {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_ty_tuple(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let TyTuple { types } = self;
|
||||||
|
types.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for TyRef {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_ty_ref(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let TyRef { mutable, count: _, to } = self;
|
||||||
|
mutable.children(v);
|
||||||
|
to.children(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for TyPtr {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_ty_ptr(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let TyPtr { to } = self;
|
||||||
|
to.children(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for TyFn {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_ty_fn(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let TyFn { args, rety } = self;
|
||||||
|
args.visit_in(v);
|
||||||
|
rety.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Path {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_path(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Path { absolute: _, parts } = self;
|
||||||
|
parts.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for PathPart {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_path_part(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
match self {
|
||||||
|
PathPart::SuperKw => {}
|
||||||
|
PathPart::SelfTy => {}
|
||||||
|
PathPart::Ident(sym) => sym.visit_in(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Stmt {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_stmt(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Stmt { span, kind, semi } = self;
|
||||||
|
span.visit_in(v);
|
||||||
|
kind.visit_in(v);
|
||||||
|
semi.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for StmtKind {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_stmt_kind(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
match self {
|
||||||
|
StmtKind::Empty => {}
|
||||||
|
StmtKind::Item(value) => value.visit_in(v),
|
||||||
|
StmtKind::Expr(value) => value.visit_in(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Semi {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_semi(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Expr {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_expr(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Expr { span, kind } = self;
|
||||||
|
span.visit_in(v);
|
||||||
|
kind.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for ExprKind {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_expr_kind(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
match self {
|
||||||
|
ExprKind::Empty => {}
|
||||||
|
ExprKind::Closure(value) => value.visit_in(v),
|
||||||
|
ExprKind::Tuple(value) => value.visit_in(v),
|
||||||
|
ExprKind::Structor(value) => value.visit_in(v),
|
||||||
|
ExprKind::Array(value) => value.visit_in(v),
|
||||||
|
ExprKind::ArrayRep(value) => value.visit_in(v),
|
||||||
|
ExprKind::AddrOf(value) => value.visit_in(v),
|
||||||
|
ExprKind::Quote(value) => value.visit_in(v),
|
||||||
|
ExprKind::Literal(value) => value.visit_in(v),
|
||||||
|
ExprKind::Group(value) => value.visit_in(v),
|
||||||
|
ExprKind::Block(value) => value.visit_in(v),
|
||||||
|
ExprKind::Assign(value) => value.visit_in(v),
|
||||||
|
ExprKind::Modify(value) => value.visit_in(v),
|
||||||
|
ExprKind::Binary(value) => value.visit_in(v),
|
||||||
|
ExprKind::Unary(value) => value.visit_in(v),
|
||||||
|
ExprKind::Member(value) => value.visit_in(v),
|
||||||
|
ExprKind::Index(value) => value.visit_in(v),
|
||||||
|
ExprKind::Cast(value) => value.visit_in(v),
|
||||||
|
ExprKind::Path(value) => value.visit_in(v),
|
||||||
|
ExprKind::Let(value) => value.visit_in(v),
|
||||||
|
ExprKind::Match(value) => value.visit_in(v),
|
||||||
|
ExprKind::While(value) => value.visit_in(v),
|
||||||
|
ExprKind::If(value) => value.visit_in(v),
|
||||||
|
ExprKind::For(value) => value.visit_in(v),
|
||||||
|
ExprKind::Break(value) => value.visit_in(v),
|
||||||
|
ExprKind::Return(value) => value.visit_in(v),
|
||||||
|
ExprKind::Continue => v.visit_continue(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Walk for Closure {
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_closure(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Self { arg, body } = self;
|
||||||
|
v.visit_pattern(arg);
|
||||||
|
v.visit_expr(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Walk for Tuple {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_tuple(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Tuple { exprs } = self;
|
||||||
|
exprs.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Structor {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_structor(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Structor { to, init } = self;
|
||||||
|
to.visit_in(v);
|
||||||
|
init.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Fielder {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_fielder(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Fielder { name, init } = self;
|
||||||
|
name.visit_in(v);
|
||||||
|
init.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Array {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_array(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Array { values } = self;
|
||||||
|
values.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for ArrayRep {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_array_rep(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let ArrayRep { value, repeat: _ } = self;
|
||||||
|
value.visit_in(v);
|
||||||
|
// repeat.visit_in(v) // TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for AddrOf {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_addrof(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let AddrOf { mutable, expr } = self;
|
||||||
|
mutable.visit_in(v);
|
||||||
|
expr.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Cast {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_cast(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Cast { head, ty } = self;
|
||||||
|
head.visit_in(v);
|
||||||
|
ty.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Quote {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_quote(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Quote { quote } = self;
|
||||||
|
quote.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Group {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_group(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Group { expr } = self;
|
||||||
|
expr.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Block {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_block(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Block { stmts } = self;
|
||||||
|
stmts.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Assign {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_assign(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Assign { parts } = self;
|
||||||
|
parts.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Modify {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_modify(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Modify { kind, parts } = self;
|
||||||
|
kind.visit_in(v);
|
||||||
|
parts.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for ModifyKind {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_modify_kind(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Binary {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_binary(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Binary { kind, parts } = self;
|
||||||
|
kind.visit_in(v);
|
||||||
|
parts.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for BinaryKind {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_binary_kind(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Unary {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_unary(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Unary { kind, tail } = self;
|
||||||
|
kind.visit_in(v);
|
||||||
|
tail.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for UnaryKind {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_unary_kind(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Member {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_member(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Member { head, kind } = self;
|
||||||
|
head.visit_in(v);
|
||||||
|
kind.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for MemberKind {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_member_kind(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
match self {
|
||||||
|
MemberKind::Call(sym, tuple) => {
|
||||||
|
sym.visit_in(v);
|
||||||
|
tuple.visit_in(v);
|
||||||
|
}
|
||||||
|
MemberKind::Struct(sym) => sym.visit_in(v),
|
||||||
|
MemberKind::Tuple(literal) => literal.visit_in(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Index {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_index(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Index { head, indices } = self;
|
||||||
|
head.visit_in(v);
|
||||||
|
indices.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Let {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_let(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Let { mutable, name, ty, init } = self;
|
||||||
|
mutable.visit_in(v);
|
||||||
|
name.visit_in(v);
|
||||||
|
ty.visit_in(v);
|
||||||
|
init.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Match {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_match(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Match { scrutinee, arms } = self;
|
||||||
|
scrutinee.visit_in(v);
|
||||||
|
arms.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for MatchArm {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_match_arm(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let MatchArm(pat, expr) = self;
|
||||||
|
pat.visit_in(v);
|
||||||
|
expr.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Pattern {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_pattern(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
match self {
|
||||||
|
Pattern::Name(sym) => sym.visit_in(v),
|
||||||
|
Pattern::Path(path) => path.visit_in(v),
|
||||||
|
Pattern::Literal(literal) => literal.visit_in(v),
|
||||||
|
Pattern::Rest(pattern) => pattern.visit_in(v),
|
||||||
|
Pattern::Ref(mutability, pattern) => {
|
||||||
|
mutability.visit_in(v);
|
||||||
|
pattern.visit_in(v);
|
||||||
|
}
|
||||||
|
Pattern::RangeExc(from, to) => {
|
||||||
|
from.visit_in(v);
|
||||||
|
to.visit_in(v);
|
||||||
|
}
|
||||||
|
Pattern::RangeInc(from, to) => {
|
||||||
|
from.visit_in(v);
|
||||||
|
to.visit_in(v);
|
||||||
|
}
|
||||||
|
Pattern::Tuple(patterns) => patterns.visit_in(v),
|
||||||
|
Pattern::Array(patterns) => patterns.visit_in(v),
|
||||||
|
Pattern::Struct(path, items) => {
|
||||||
|
path.visit_in(v);
|
||||||
|
items.visit_in(v);
|
||||||
|
}
|
||||||
|
Pattern::TupleStruct(path, patterns) => {
|
||||||
|
path.visit_in(v);
|
||||||
|
patterns.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for While {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_while(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let While { cond, pass, fail } = self;
|
||||||
|
cond.visit_in(v);
|
||||||
|
pass.visit_in(v);
|
||||||
|
fail.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for If {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_if(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let If { cond, pass, fail } = self;
|
||||||
|
cond.visit_in(v);
|
||||||
|
pass.visit_in(v);
|
||||||
|
fail.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for For {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_for(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let For { bind, cond, pass, fail } = self;
|
||||||
|
cond.visit_in(v);
|
||||||
|
fail.visit_in(v);
|
||||||
|
bind.visit_in(v);
|
||||||
|
pass.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Else {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_else(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Else { body } = self;
|
||||||
|
body.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Break {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_break(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Break { body } = self;
|
||||||
|
body.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Walk for Return {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
v.visit_return(self);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let Return { body } = self;
|
||||||
|
body.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- BLANKET IMPLEMENTATIONS
|
||||||
|
|
||||||
|
impl<T: Walk> Walk for [T] {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
self.iter().for_each(|value| value.visit_in(v));
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
self.iter().for_each(|value| value.children(v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Walk> Walk for Vec<T> {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
self.as_slice().visit_in(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
self.as_slice().children(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: Walk, B: Walk> Walk for (A, B) {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let (a, b) = self;
|
||||||
|
a.visit_in(v);
|
||||||
|
b.visit_in(v);
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
let (a, b) = self;
|
||||||
|
a.children(v);
|
||||||
|
b.children(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Walk> Walk for Option<T> {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
if let Some(value) = self.as_ref() {
|
||||||
|
value.visit_in(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
if let Some(value) = self {
|
||||||
|
value.children(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Walk> Walk for Box<T> {
|
||||||
|
#[inline]
|
||||||
|
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
self.as_ref().visit_in(v)
|
||||||
|
}
|
||||||
|
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
|
||||||
|
self.as_ref().children(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
11
compiler/cl-ast/src/desugar.rs
Normal file
11
compiler/cl-ast/src/desugar.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
//! Desugaring passes for Conlang
|
||||||
|
|
||||||
|
pub mod constant_folder;
|
||||||
|
pub mod path_absoluter;
|
||||||
|
pub mod squash_groups;
|
||||||
|
pub mod while_else;
|
||||||
|
|
||||||
|
pub use constant_folder::ConstantFolder;
|
||||||
|
pub use path_absoluter::NormalizePaths;
|
||||||
|
pub use squash_groups::SquashGroups;
|
||||||
|
pub use while_else::WhileElseDesugar;
|
||||||
90
compiler/cl-ast/src/desugar/constant_folder.rs
Normal file
90
compiler/cl-ast/src/desugar/constant_folder.rs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
use crate::{
|
||||||
|
ast::{ExprKind as Ek, *},
|
||||||
|
ast_visitor::{Fold, fold::or_fold_expr_kind},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct ConstantFolder;
|
||||||
|
|
||||||
|
macro bin_rule(
|
||||||
|
match ($kind: ident, $head: expr, $tail: expr) {
|
||||||
|
$(($op:ident, $impl:expr, $($ty:ident -> $rety:ident),*)),*$(,)?
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
#[allow(clippy::all)]
|
||||||
|
match ($kind, $head, $tail) {
|
||||||
|
$($(( BinaryKind::$op,
|
||||||
|
Expr { kind: ExprKind::Literal(Literal::$ty(a)), .. },
|
||||||
|
Expr { kind: ExprKind::Literal(Literal::$ty(b)), .. },
|
||||||
|
) => {
|
||||||
|
ExprKind::Literal(Literal::$rety($impl(a, b)))
|
||||||
|
},)*)*
|
||||||
|
(kind, head, tail) => ExprKind::Binary(Binary {
|
||||||
|
kind,
|
||||||
|
parts: Box::new((head, tail)),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro un_rule(
|
||||||
|
match ($kind: ident, $tail: expr) {
|
||||||
|
$(($op:ident, $impl:expr, $($ty:ident),*)),*$(,)?
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
match ($kind, $tail) {
|
||||||
|
$($((UnaryKind::$op, Expr { kind: ExprKind::Literal(Literal::$ty(v)), .. }) => {
|
||||||
|
ExprKind::Literal(Literal::$ty($impl(v)))
|
||||||
|
},)*)*
|
||||||
|
(kind, tail) => ExprKind::Unary(Unary { kind, tail: Box::new(tail) }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fold for ConstantFolder {
|
||||||
|
fn fold_expr_kind(&mut self, kind: Ek) -> Ek {
|
||||||
|
match kind {
|
||||||
|
Ek::Group(Group { expr }) => self.fold_expr_kind(expr.kind),
|
||||||
|
Ek::Binary(Binary { kind, parts }) => {
|
||||||
|
let (head, tail) = *parts;
|
||||||
|
bin_rule! (match (kind, self.fold_expr(head), self.fold_expr(tail)) {
|
||||||
|
(Lt, |a, b| a < b, Bool -> Bool, Int -> Bool),
|
||||||
|
(LtEq, |a, b| a <= b, Bool -> Bool, Int -> Bool),
|
||||||
|
(Equal, |a, b| a == b, Bool -> Bool, Int -> Bool),
|
||||||
|
(NotEq, |a, b| a != b, Bool -> Bool, Int -> Bool),
|
||||||
|
(GtEq, |a, b| a >= b, Bool -> Bool, Int -> Bool),
|
||||||
|
(Gt, |a, b| a > b, Bool -> Bool, Int -> Bool),
|
||||||
|
(BitAnd, |a, b| a & b, Bool -> Bool, Int -> Int),
|
||||||
|
(BitOr, |a, b| a | b, Bool -> Bool, Int -> Int),
|
||||||
|
(BitXor, |a, b| a ^ b, Bool -> Bool, Int -> Int),
|
||||||
|
(Shl, |a, b| a << b, Int -> Int),
|
||||||
|
(Shr, |a, b| a >> b, Int -> Int),
|
||||||
|
(Add, |a, b| a + b, Int -> Int),
|
||||||
|
(Sub, |a, b| a - b, Int -> Int),
|
||||||
|
(Mul, |a, b| a * b, Int -> Int),
|
||||||
|
(Div, |a, b| a / b, Int -> Int),
|
||||||
|
(Rem, |a, b| a % b, Int -> Int),
|
||||||
|
// Cursed bit-smuggled float shenanigans
|
||||||
|
(Lt, |a, b| f64::from_bits(a) < f64::from_bits(b), Float -> Bool),
|
||||||
|
(LtEq, |a, b| f64::from_bits(a) >= f64::from_bits(b), Float -> Bool),
|
||||||
|
(Equal, |a, b| f64::from_bits(a) == f64::from_bits(b), Float -> Bool),
|
||||||
|
(NotEq, |a, b| f64::from_bits(a) != f64::from_bits(b), Float -> Bool),
|
||||||
|
(GtEq, |a, b| f64::from_bits(a) <= f64::from_bits(b), Float -> Bool),
|
||||||
|
(Gt, |a, b| f64::from_bits(a) > f64::from_bits(b), Float -> Bool),
|
||||||
|
(Add, |a, b| (f64::from_bits(a) + f64::from_bits(b)).to_bits(), Float -> Float),
|
||||||
|
(Sub, |a, b| (f64::from_bits(a) - f64::from_bits(b)).to_bits(), Float -> Float),
|
||||||
|
(Mul, |a, b| (f64::from_bits(a) * f64::from_bits(b)).to_bits(), Float -> Float),
|
||||||
|
(Div, |a, b| (f64::from_bits(a) / f64::from_bits(b)).to_bits(), Float -> Float),
|
||||||
|
(Rem, |a, b| (f64::from_bits(a) % f64::from_bits(b)).to_bits(), Float -> Float),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Ek::Unary(Unary { kind, tail }) => {
|
||||||
|
un_rule! (match (kind, self.fold_expr(*tail)) {
|
||||||
|
(Not, std::ops::Not::not, Int, Bool),
|
||||||
|
(Neg, std::ops::Not::not, Bool),
|
||||||
|
(Neg, |i| -(i as i128) as u128, Int),
|
||||||
|
(Neg, |f| (-f64::from_bits(f)).to_bits(), Float),
|
||||||
|
(At, std::ops::Not::not, Float), /* Lmao */
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => or_fold_expr_kind(self, kind),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
55
compiler/cl-ast/src/desugar/path_absoluter.rs
Normal file
55
compiler/cl-ast/src/desugar/path_absoluter.rs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
use crate::{ast::*, ast_visitor::Fold};
|
||||||
|
|
||||||
|
/// Converts relative paths into absolute paths
|
||||||
|
pub struct NormalizePaths {
|
||||||
|
path: Path,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NormalizePaths {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { path: Path { absolute: true, parts: vec![] } }
|
||||||
|
}
|
||||||
|
/// Normalizes paths as if they came from within the provided paths
|
||||||
|
pub fn in_path(path: Path) -> Self {
|
||||||
|
Self { path }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for NormalizePaths {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fold for NormalizePaths {
|
||||||
|
fn fold_module(&mut self, m: Module) -> Module {
|
||||||
|
let Module { name, file } = m;
|
||||||
|
self.path.push(PathPart::Ident(name));
|
||||||
|
|
||||||
|
let name = self.fold_sym(name);
|
||||||
|
let file = file.map(|f| self.fold_file(f));
|
||||||
|
|
||||||
|
self.path.pop();
|
||||||
|
Module { name, file }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_path(&mut self, p: Path) -> Path {
|
||||||
|
if p.absolute {
|
||||||
|
p
|
||||||
|
} else {
|
||||||
|
self.path.clone().concat(&p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_use(&mut self, u: Use) -> Use {
|
||||||
|
let Use { absolute, mut tree } = u;
|
||||||
|
|
||||||
|
if !absolute {
|
||||||
|
for segment in self.path.parts.iter().rev() {
|
||||||
|
tree = UseTree::Path(*segment, Box::new(tree))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Use { absolute: true, tree: self.fold_use_tree(tree) }
|
||||||
|
}
|
||||||
|
}
|
||||||
14
compiler/cl-ast/src/desugar/squash_groups.rs
Normal file
14
compiler/cl-ast/src/desugar/squash_groups.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
//! Squashes group expressions
|
||||||
|
use crate::{ast::*, ast_visitor::fold::*};
|
||||||
|
|
||||||
|
/// Squashes group expressions
|
||||||
|
pub struct SquashGroups;
|
||||||
|
|
||||||
|
impl Fold for SquashGroups {
|
||||||
|
fn fold_expr_kind(&mut self, kind: ExprKind) -> ExprKind {
|
||||||
|
match kind {
|
||||||
|
ExprKind::Group(Group { expr }) => self.fold_expr(*expr).kind,
|
||||||
|
_ => or_fold_expr_kind(self, kind),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
compiler/cl-ast/src/desugar/while_else.rs
Normal file
37
compiler/cl-ast/src/desugar/while_else.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
//! Desugars `while {...} else` expressions
|
||||||
|
//! into `loop if {...} else break` expressions
|
||||||
|
|
||||||
|
use crate::{ast::*, ast_visitor::fold::Fold};
|
||||||
|
use cl_structures::span::Span;
|
||||||
|
|
||||||
|
/// Desugars while-else expressions
|
||||||
|
/// into loop-if-else-break expressions
|
||||||
|
pub struct WhileElseDesugar;
|
||||||
|
|
||||||
|
impl Fold for WhileElseDesugar {
|
||||||
|
fn fold_expr(&mut self, e: Expr) -> Expr {
|
||||||
|
let Expr { span, kind } = e;
|
||||||
|
let kind = desugar_while(span, kind);
|
||||||
|
Expr { span: self.fold_span(span), kind: self.fold_expr_kind(kind) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Desugars while(-else) expressions into loop-if-else-break expressions
|
||||||
|
fn desugar_while(span: Span, kind: ExprKind) -> ExprKind {
|
||||||
|
match kind {
|
||||||
|
// work backwards: fail -> break -> if -> loop
|
||||||
|
ExprKind::While(While { cond, pass, fail: Else { body } }) => {
|
||||||
|
// Preserve the else-expression's span, if present, or use the parent's span
|
||||||
|
let fail_span = body.as_ref().map(|body| body.span).unwrap_or(span);
|
||||||
|
let break_expr = Expr { span: fail_span, kind: ExprKind::Break(Break { body }) };
|
||||||
|
|
||||||
|
let loop_body = If { cond, pass, fail: Else { body: Some(Box::new(break_expr)) } };
|
||||||
|
let loop_body = ExprKind::If(loop_body);
|
||||||
|
ExprKind::Unary(Unary {
|
||||||
|
kind: UnaryKind::Loop,
|
||||||
|
tail: Box::new(Expr { span, kind: loop_body }),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => kind,
|
||||||
|
}
|
||||||
|
}
|
||||||
82
compiler/cl-ast/src/format.rs
Normal file
82
compiler/cl-ast/src/format.rs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
use delimiters::Delimiters;
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
impl<W: Write + ?Sized> FmtAdapter for W {}
|
||||||
|
pub trait FmtAdapter: Write {
|
||||||
|
fn indent(&mut self) -> Indent<'_, Self> {
|
||||||
|
Indent { f: self }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delimit(&mut self, delim: Delimiters) -> Delimit<'_, Self> {
|
||||||
|
Delimit::new(self, delim)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delimit_with(&mut self, open: &'static str, close: &'static str) -> Delimit<'_, Self> {
|
||||||
|
Delimit::new(self, Delimiters { open, close })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pads text with leading indentation after every newline
|
||||||
|
pub struct Indent<'f, F: Write + ?Sized> {
|
||||||
|
f: &'f mut F,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Write + ?Sized> Write for Indent<'_, F> {
|
||||||
|
fn write_str(&mut self, s: &str) -> std::fmt::Result {
|
||||||
|
for s in s.split_inclusive('\n') {
|
||||||
|
self.f.write_str(s)?;
|
||||||
|
if s.ends_with('\n') {
|
||||||
|
self.f.write_str(" ")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prints [Delimiters] around anything formatted with this. Implies [Indent]
|
||||||
|
pub struct Delimit<'f, F: Write + ?Sized> {
|
||||||
|
f: Indent<'f, F>,
|
||||||
|
delim: Delimiters,
|
||||||
|
}
|
||||||
|
impl<'f, F: Write + ?Sized> Delimit<'f, F> {
|
||||||
|
pub fn new(f: &'f mut F, delim: Delimiters) -> Self {
|
||||||
|
let mut f = f.indent();
|
||||||
|
let _ = f.write_str(delim.open);
|
||||||
|
Self { f, delim }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<F: Write + ?Sized> Drop for Delimit<'_, F> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let Self { f: Indent { f, .. }, delim } = self;
|
||||||
|
let _ = f.write_str(delim.close);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Write + ?Sized> Write for Delimit<'_, F> {
|
||||||
|
fn write_str(&mut self, s: &str) -> std::fmt::Result {
|
||||||
|
self.f.write_str(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod delimiters {
|
||||||
|
#![allow(dead_code)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Delimiters {
|
||||||
|
pub open: &'static str,
|
||||||
|
pub close: &'static str,
|
||||||
|
}
|
||||||
|
/// Delimits with braces decorated with spaces `" {\n"`, ..., `"\n}"`
|
||||||
|
pub const SPACED_BRACES: Delimiters = Delimiters { open: " {\n", close: "\n}" };
|
||||||
|
/// Delimits with braces on separate lines `{\n`, ..., `\n}`
|
||||||
|
pub const BRACES: Delimiters = Delimiters { open: "{\n", close: "\n}" };
|
||||||
|
/// Delimits with parentheses on separate lines `{\n`, ..., `\n}`
|
||||||
|
pub const PARENS: Delimiters = Delimiters { open: "(\n", close: "\n)" };
|
||||||
|
/// Delimits with square brackets on separate lines `{\n`, ..., `\n}`
|
||||||
|
pub const SQUARE: Delimiters = Delimiters { open: "[\n", close: "\n]" };
|
||||||
|
/// Delimits with braces on the same line `{ `, ..., ` }`
|
||||||
|
pub const INLINE_BRACES: Delimiters = Delimiters { open: "{ ", close: " }" };
|
||||||
|
/// Delimits with parentheses on the same line `( `, ..., ` )`
|
||||||
|
pub const INLINE_PARENS: Delimiters = Delimiters { open: "(", close: ")" };
|
||||||
|
/// Delimits with square brackets on the same line `[ `, ..., ` ]`
|
||||||
|
pub const INLINE_SQUARE: Delimiters = Delimiters { open: "[", close: "]" };
|
||||||
|
}
|
||||||
23
compiler/cl-ast/src/lib.rs
Normal file
23
compiler/cl-ast/src/lib.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
//! # The Abstract Syntax Tree
|
||||||
|
//! Contains definitions of Conlang AST Nodes.
|
||||||
|
//!
|
||||||
|
//! # Notable nodes
|
||||||
|
//! - [Item] and [ItemKind]: Top-level constructs
|
||||||
|
//! - [Stmt] and [StmtKind]: Statements
|
||||||
|
//! - [Expr] and [ExprKind]: Expressions
|
||||||
|
//! - [Assign], [Binary], and [Unary] expressions
|
||||||
|
//! - [ModifyKind], [BinaryKind], and [UnaryKind] operators
|
||||||
|
//! - [Ty] and [TyKind]: Type qualifiers
|
||||||
|
//! - [Pattern]: Pattern matching operators
|
||||||
|
//! - [Path]: Path expressions
|
||||||
|
#![warn(clippy::all)]
|
||||||
|
#![feature(decl_macro)]
|
||||||
|
|
||||||
|
pub use ast::*;
|
||||||
|
pub use ast_impl::weight_of::WeightOf;
|
||||||
|
|
||||||
|
pub mod ast;
|
||||||
|
pub mod ast_impl;
|
||||||
|
pub mod ast_visitor;
|
||||||
|
pub mod desugar;
|
||||||
|
pub mod format;
|
||||||
18
compiler/cl-embed/Cargo.toml
Normal file
18
compiler/cl-embed/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
name = "cl-embed"
|
||||||
|
version = "0.1.0"
|
||||||
|
repository.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
publish.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cl-interpret = { path = "../cl-interpret" }
|
||||||
|
cl-ast = { path = "../cl-ast" }
|
||||||
|
cl-structures = { path = "../cl-structures" }
|
||||||
|
cl-lexer = { path = "../cl-lexer" }
|
||||||
|
cl-parser = { path = "../cl-parser" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
repline = { version = "*", registry = "soft-fish" }
|
||||||
27
compiler/cl-embed/examples/calculator.rs
Normal file
27
compiler/cl-embed/examples/calculator.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
//! Demonstrates the cl_embed library
|
||||||
|
|
||||||
|
use cl_embed::*;
|
||||||
|
use repline::{Response, prebaked};
|
||||||
|
|
||||||
|
fn main() -> Result<(), repline::Error> {
|
||||||
|
let mut env = Environment::new();
|
||||||
|
|
||||||
|
if let Err(e) = conlang_include!("calculator/expression.cl")(&mut env) {
|
||||||
|
panic!("{e}")
|
||||||
|
}
|
||||||
|
|
||||||
|
prebaked::read_and("", "calc >", " ? >", |line| {
|
||||||
|
env.bind("line", line);
|
||||||
|
|
||||||
|
let res = conlang! {
|
||||||
|
|
||||||
|
let (expr, rest) = parse(line.chars(), Power::None);
|
||||||
|
execute(expr)
|
||||||
|
|
||||||
|
}(&mut env)?;
|
||||||
|
|
||||||
|
println!("{res}");
|
||||||
|
|
||||||
|
Ok(Response::Accept)
|
||||||
|
})
|
||||||
|
}
|
||||||
1
compiler/cl-embed/examples/calculator/expression.cl
Symbolic link
1
compiler/cl-embed/examples/calculator/expression.cl
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../../sample-code/calculator.cl
|
||||||
182
compiler/cl-embed/src/lib.rs
Normal file
182
compiler/cl-embed/src/lib.rs
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
//! Embed Conlang code into your Rust project!
|
||||||
|
//!
|
||||||
|
//! # This crate is experimental, and has no guarantees of stability.
|
||||||
|
#![feature(decl_macro)]
|
||||||
|
#![cfg_attr(test, feature(assert_matches))]
|
||||||
|
#![allow(unused_imports)]
|
||||||
|
|
||||||
|
pub use cl_interpret::{convalue::ConValue as Value, env::Environment};
|
||||||
|
|
||||||
|
use cl_ast::{Block, File, Module, ast_visitor::Fold};
|
||||||
|
use cl_interpret::{convalue::ConValue, interpret::Interpret};
|
||||||
|
use cl_lexer::Lexer;
|
||||||
|
use cl_parser::{Parser, error::Error as ParseError, inliner::ModuleInliner};
|
||||||
|
use std::{path::Path, sync::OnceLock};
|
||||||
|
|
||||||
|
/// Constructs a function which evaluates a Conlang Block
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Bind and use a variable
|
||||||
|
/// ```rust
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// use cl_embed::{conlang, Environment, Value};
|
||||||
|
///
|
||||||
|
/// let mut env = Environment::new();
|
||||||
|
///
|
||||||
|
/// // Bind a variable named `message` to "Hello, world!"
|
||||||
|
/// env.bind("message", "Hello, World!");
|
||||||
|
///
|
||||||
|
/// let print_hello = conlang!{
|
||||||
|
/// println(message);
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// // Run the function
|
||||||
|
/// let ret = print_hello(&mut env)?;
|
||||||
|
///
|
||||||
|
/// // `println` returns Empty
|
||||||
|
/// assert!(matches!(ret, Value::Empty));
|
||||||
|
///
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub macro conlang(
|
||||||
|
$($t:tt)*
|
||||||
|
) {{
|
||||||
|
// Parse once
|
||||||
|
static FN: OnceLock<Result<Block, ParseError>> = OnceLock::new();
|
||||||
|
|
||||||
|
|env: &mut Environment| -> Result<ConValue, EvalError> {
|
||||||
|
FN.get_or_init(|| {
|
||||||
|
// TODO: embed the full module tree at compile time
|
||||||
|
let path =
|
||||||
|
AsRef::<Path>::as_ref(&concat!(env!("CARGO_MANIFEST_DIR"), "/../../", file!()))
|
||||||
|
.with_extension("");
|
||||||
|
let mut mi = ModuleInliner::new(path);
|
||||||
|
let code = mi.fold_block(
|
||||||
|
Parser::new(
|
||||||
|
concat!(file!(), ":", line!(), ":", column!()),
|
||||||
|
Lexer::new(stringify!({ $($t)* })),
|
||||||
|
)
|
||||||
|
.parse::<Block>()?,
|
||||||
|
);
|
||||||
|
if let Some((ie, pe)) = mi.into_errs() {
|
||||||
|
for (file, err) in ie {
|
||||||
|
eprintln!("{}: {err}", file.display());
|
||||||
|
}
|
||||||
|
for (file, err) in pe {
|
||||||
|
eprintln!("{}: {err}", file.display());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(code)
|
||||||
|
})
|
||||||
|
.as_ref()
|
||||||
|
.map_err(Clone::clone)?
|
||||||
|
.interpret(env)
|
||||||
|
.map_err(Into::into)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
pub macro conlang_include{
|
||||||
|
($path:literal, $name:ident) => {
|
||||||
|
|env: &mut Environment| -> Result<ConValue, EvalError> {
|
||||||
|
// TODO: embed the full module tree at compile time
|
||||||
|
let path = AsRef::<Path>::as_ref(&concat!(env!("CARGO_MANIFEST_DIR"), "/../../", file!()))
|
||||||
|
.with_file_name(concat!($path));
|
||||||
|
let mut mi = ModuleInliner::new(path);
|
||||||
|
let code = mi.fold_module(Module {
|
||||||
|
name: stringify!($name).into(),
|
||||||
|
file: Some(
|
||||||
|
Parser::new(
|
||||||
|
concat!(file!(), ":", line!(), ":", column!()),
|
||||||
|
Lexer::new(include_str!($path)),
|
||||||
|
)
|
||||||
|
.parse()?,
|
||||||
|
),
|
||||||
|
});
|
||||||
|
if let Some((ie, pe)) = mi.into_errs() {
|
||||||
|
for (file, err) in ie {
|
||||||
|
eprintln!("{}: {err}", file.display());
|
||||||
|
}
|
||||||
|
for (file, err) in pe {
|
||||||
|
eprintln!("{}: {err}", file.display());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
code.interpret(env).map_err(Into::into)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
($path:literal) => {
|
||||||
|
|env: &mut Environment| -> Result<ConValue, EvalError> {
|
||||||
|
// TODO: embed the full module tree at compile time
|
||||||
|
let path = AsRef::<Path>::as_ref(&concat!(env!("CARGO_MANIFEST_DIR"), "/../../", file!()))
|
||||||
|
.with_file_name(concat!($path));
|
||||||
|
let mut mi = ModuleInliner::new(path);
|
||||||
|
let code = mi.fold_file(
|
||||||
|
Parser::new(
|
||||||
|
concat!(file!(), ":", line!(), ":", column!()),
|
||||||
|
Lexer::new(include_str!($path)),
|
||||||
|
)
|
||||||
|
.parse()?,
|
||||||
|
);
|
||||||
|
if let Some((ie, pe)) = mi.into_errs() {
|
||||||
|
for (file, err) in ie {
|
||||||
|
eprintln!("{}: {err}", file.display());
|
||||||
|
}
|
||||||
|
for (file, err) in pe {
|
||||||
|
eprintln!("{}: {err}", file.display());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
code.interpret(env).map_err(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum EvalError {
|
||||||
|
Parse(cl_parser::error::Error),
|
||||||
|
Interpret(cl_interpret::error::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<cl_parser::error::Error> for EvalError {
|
||||||
|
fn from(value: cl_parser::error::Error) -> Self {
|
||||||
|
Self::Parse(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<cl_interpret::error::Error> for EvalError {
|
||||||
|
fn from(value: cl_interpret::error::Error) -> Self {
|
||||||
|
Self::Interpret(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::error::Error for EvalError {}
|
||||||
|
impl std::fmt::Display for EvalError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
EvalError::Parse(error) => error.fmt(f),
|
||||||
|
EvalError::Interpret(error) => error.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::assert_matches::assert_matches;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_works() -> Result<(), EvalError> {
|
||||||
|
let mut env = Environment::new();
|
||||||
|
|
||||||
|
let result = conlang! {
|
||||||
|
fn add(left, right) -> isize {
|
||||||
|
left + right
|
||||||
|
}
|
||||||
|
|
||||||
|
add(2, 2)
|
||||||
|
}(&mut env);
|
||||||
|
|
||||||
|
assert_matches!(result, Ok(Value::Int(4)));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
59
compiler/cl-interpret/examples/conlang-run.rs
Normal file
59
compiler/cl-interpret/examples/conlang-run.rs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
//! A bare-minimum harness to evaluate a Conlang program
|
||||||
|
|
||||||
|
use std::{error::Error, path::PathBuf};
|
||||||
|
|
||||||
|
use cl_ast::Expr;
|
||||||
|
use cl_interpret::{convalue::ConValue, env::Environment};
|
||||||
|
use cl_lexer::Lexer;
|
||||||
|
use cl_parser::{Parser, inliner::ModuleInliner};
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut args = std::env::args();
|
||||||
|
|
||||||
|
let prog = args.next().unwrap();
|
||||||
|
let Some(path) = args.next().map(PathBuf::from) else {
|
||||||
|
println!("Usage: {prog} `file.cl` [ args... ]");
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
let parent = path.parent().unwrap_or("".as_ref());
|
||||||
|
|
||||||
|
let code = std::fs::read_to_string(&path)?;
|
||||||
|
let code = Parser::new(path.display().to_string(), Lexer::new(&code)).parse()?;
|
||||||
|
let code = match ModuleInliner::new(parent).inline(code) {
|
||||||
|
Ok(code) => code,
|
||||||
|
Err((code, ioerrs, perrs)) => {
|
||||||
|
for (p, err) in ioerrs {
|
||||||
|
eprintln!("{}:{err}", p.display());
|
||||||
|
}
|
||||||
|
for (p, err) in perrs {
|
||||||
|
eprintln!("{}:{err}", p.display());
|
||||||
|
}
|
||||||
|
code
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut env = Environment::new();
|
||||||
|
env.eval(&code)?;
|
||||||
|
|
||||||
|
let main = "main".into();
|
||||||
|
if env.get(main).is_ok() {
|
||||||
|
let args = args
|
||||||
|
.flat_map(|arg| {
|
||||||
|
Parser::new(&arg, Lexer::new(&arg))
|
||||||
|
.parse::<Expr>()
|
||||||
|
.map(|arg| env.eval(&arg))
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
match env.call(main, &args) {
|
||||||
|
Ok(ConValue::Empty) => {}
|
||||||
|
Ok(retval) => println!("{retval}"),
|
||||||
|
Err(e) => {
|
||||||
|
panic!("{e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -8,9 +8,5 @@ fn main() {
|
|||||||
|
|
||||||
/// Implements the classic recursive definition of fib()
|
/// Implements the classic recursive definition of fib()
|
||||||
fn fib(a: i64) -> i64 {
|
fn fib(a: i64) -> i64 {
|
||||||
if a > 1 {
|
if a > 1 { fib(a - 1) + fib(a - 2) } else { a }
|
||||||
fib(a - 1) + fib(a - 2)
|
|
||||||
} else {
|
|
||||||
1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
410
compiler/cl-interpret/src/builtin.rs
Normal file
410
compiler/cl-interpret/src/builtin.rs
Normal file
@@ -0,0 +1,410 @@
|
|||||||
|
#![allow(non_upper_case_globals)]
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
convalue::ConValue,
|
||||||
|
env::Environment,
|
||||||
|
error::{Error, IResult},
|
||||||
|
};
|
||||||
|
use std::io::{Write, stdout};
|
||||||
|
|
||||||
|
/// A function built into the interpreter.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Builtin {
|
||||||
|
/// An identifier to be used during registration
|
||||||
|
pub name: &'static str,
|
||||||
|
/// The signature, displayed when the builtin is printed
|
||||||
|
pub desc: &'static str,
|
||||||
|
/// The function to be run when called
|
||||||
|
pub func: &'static dyn Fn(&mut Environment, &[ConValue]) -> IResult<ConValue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Builtin {
|
||||||
|
/// Constructs a new Builtin
|
||||||
|
pub const fn new(
|
||||||
|
name: &'static str,
|
||||||
|
desc: &'static str,
|
||||||
|
func: &'static impl Fn(&mut Environment, &[ConValue]) -> IResult<ConValue>,
|
||||||
|
) -> Builtin {
|
||||||
|
Builtin { name, desc, func }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn description(&self) -> &'static str {
|
||||||
|
self.desc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for Builtin {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("Builtin")
|
||||||
|
.field("description", &self.desc)
|
||||||
|
.finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Builtin {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(self.desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::Callable for Builtin {
|
||||||
|
fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
|
||||||
|
(self.func)(interpreter, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> cl_ast::Sym {
|
||||||
|
self.name.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Turns a function definition into a [Builtin].
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use cl_interpret::{builtin::builtin, convalue::ConValue};
|
||||||
|
/// let my_builtin = builtin! {
|
||||||
|
/// /// Use the `@env` suffix to bind the environment!
|
||||||
|
/// /// (needed for recursive calls)
|
||||||
|
/// fn my_builtin(ConValue::Bool(b), rest @ ..) @env {
|
||||||
|
/// // This is all Rust code!
|
||||||
|
/// eprintln!("my_builtin({b}, ..)");
|
||||||
|
/// match rest {
|
||||||
|
/// [] => Ok(ConValue::Empty),
|
||||||
|
/// _ => my_builtin(env, rest), // Can be called as a normal function!
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
pub macro builtin(
|
||||||
|
$(#[$($meta:tt)*])*
|
||||||
|
fn $name:ident ($($arg:pat),*$(,)?) $(@$env:tt)? $body:block
|
||||||
|
) {{
|
||||||
|
$(#[$($meta)*])*
|
||||||
|
fn $name(_env: &mut Environment, _args: &[ConValue]) -> IResult<ConValue> {
|
||||||
|
// Set up the builtin! environment
|
||||||
|
$(#[allow(unused)]let $env = _env;)?
|
||||||
|
// Allow for single argument `fn foo(args @ ..)` pattern
|
||||||
|
#[allow(clippy::redundant_at_rest_pattern, irrefutable_let_patterns)]
|
||||||
|
let [$($arg),*] = _args else {
|
||||||
|
Err($crate::error::Error::TypeError())?
|
||||||
|
};
|
||||||
|
$body.map(Into::into)
|
||||||
|
}
|
||||||
|
Builtin {
|
||||||
|
name: stringify!($name),
|
||||||
|
desc: stringify![builtin fn $name($($arg),*)],
|
||||||
|
func: &$name,
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
/// Constructs an array of [Builtin]s from pseudo-function definitions
|
||||||
|
pub macro builtins($(
|
||||||
|
$(#[$($meta:tt)*])*
|
||||||
|
fn $name:ident ($($args:tt)*) $(@$env:tt)? $body:block
|
||||||
|
)*) {
|
||||||
|
[$(builtin!($(#[$($meta)*])* fn $name ($($args)*) $(@$env)? $body)),*]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an [Error::BuiltinError] using interpolation of runtime expressions.
|
||||||
|
/// See [std::format].
|
||||||
|
pub macro error_format ($($t:tt)*) {
|
||||||
|
$crate::error::Error::BuiltinError(format!($($t)*))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Builtins: &[Builtin] = &builtins![
|
||||||
|
/// Unstable variadic format function
|
||||||
|
fn fmt(args @ ..) {
|
||||||
|
use std::fmt::Write;
|
||||||
|
let mut out = String::new();
|
||||||
|
if let Err(e) = args.iter().try_for_each(|arg| write!(out, "{arg}")) {
|
||||||
|
eprintln!("{e}");
|
||||||
|
}
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prints the arguments in-order, with no separators
|
||||||
|
fn print(args @ ..) {
|
||||||
|
let mut out = stdout().lock();
|
||||||
|
args.iter().try_for_each(|arg| write!(out, "{arg}") ).ok();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prints the arguments in-order, followed by a newline
|
||||||
|
fn println(args @ ..) {
|
||||||
|
let mut out = stdout().lock();
|
||||||
|
args.iter().try_for_each(|arg| write!(out, "{arg}") ).ok();
|
||||||
|
writeln!(out).ok();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Debug-prints the argument, returning a copy
|
||||||
|
fn dbg(arg) {
|
||||||
|
println!("{arg:?}");
|
||||||
|
Ok(arg.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Debug-prints the argument
|
||||||
|
fn dbgp(args @ ..) {
|
||||||
|
let mut out = stdout().lock();
|
||||||
|
args.iter().try_for_each(|arg| writeln!(out, "{arg:#?}") ).ok();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn panic(args @ ..) @env {
|
||||||
|
use std::fmt::Write;
|
||||||
|
let mut out = String::new();
|
||||||
|
if let Err(e) = args.iter().try_for_each(|arg| write!(out, "{arg}")) {
|
||||||
|
println!("{e}");
|
||||||
|
}
|
||||||
|
let mut stdout = stdout().lock();
|
||||||
|
write!(stdout, "Explicit panic: `").ok();
|
||||||
|
args.iter().try_for_each(|arg| write!(stdout, "{arg}") ).ok();
|
||||||
|
writeln!(stdout, "`").ok();
|
||||||
|
Err(Error::Panic(out))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dumps the environment
|
||||||
|
fn dump() @env {
|
||||||
|
println!("{env}");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets all global variables in the environment
|
||||||
|
fn globals() @env {
|
||||||
|
let globals = env.globals();
|
||||||
|
Ok(ConValue::Slice(globals.base, globals.binds.len()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn builtins() @env {
|
||||||
|
let len = env.globals().binds.len();
|
||||||
|
for builtin in 0..len {
|
||||||
|
if let Some(value @ ConValue::Builtin(_)) = env.get_id(builtin) {
|
||||||
|
println!("{builtin}: {value}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloca(ConValue::Int(len)) @env {
|
||||||
|
Ok(env.alloca(ConValue::Empty, *len as usize))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the length of the input list as a [ConValue::Int]
|
||||||
|
fn len(list) @env {
|
||||||
|
Ok(match list {
|
||||||
|
ConValue::Empty => 0,
|
||||||
|
ConValue::Str(s) => s.chars().count() as _,
|
||||||
|
ConValue::String(s) => s.chars().count() as _,
|
||||||
|
ConValue::Ref(r) => {
|
||||||
|
return len(env, &[env.get_id(*r).ok_or(Error::StackOverflow(*r))?.clone()])
|
||||||
|
}
|
||||||
|
ConValue::Slice(_, len) => *len as _,
|
||||||
|
ConValue::Array(arr) => arr.len() as _,
|
||||||
|
ConValue::Tuple(t) => t.len() as _,
|
||||||
|
_ => Err(Error::TypeError())?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push(ConValue::Ref(index), item) @env{
|
||||||
|
let Some(ConValue::Array(v)) = env.get_id_mut(*index) else {
|
||||||
|
Err(Error::TypeError())?
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut items = std::mem::take(v).into_vec();
|
||||||
|
items.push(item.clone());
|
||||||
|
*v = items.into_boxed_slice();
|
||||||
|
|
||||||
|
Ok(ConValue::Empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn chars(string) @env {
|
||||||
|
Ok(match string {
|
||||||
|
ConValue::Str(s) => ConValue::Array(s.chars().map(Into::into).collect()),
|
||||||
|
ConValue::String(s) => ConValue::Array(s.chars().map(Into::into).collect()),
|
||||||
|
ConValue::Ref(r) => {
|
||||||
|
return chars(env, &[env.get_id(*r).ok_or(Error::StackOverflow(*r))?.clone()])
|
||||||
|
}
|
||||||
|
_ => Err(Error::TypeError())?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump_symbols() {
|
||||||
|
println!("{}", cl_structures::intern::string_interner::StringInterner::global());
|
||||||
|
Ok(ConValue::Empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn slice_of(ConValue::Ref(arr), ConValue::Int(start)) {
|
||||||
|
Ok(ConValue::Slice(*arr, *start as usize))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a shark
|
||||||
|
fn shark() {
|
||||||
|
Ok('\u{1f988}')
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
pub const Math: &[Builtin] = &builtins![
|
||||||
|
/// Multiplication `a * b`
|
||||||
|
fn mul(lhs, rhs) {
|
||||||
|
Ok(match (lhs, rhs) {
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a * b),
|
||||||
|
_ => Err(Error::TypeError())?
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Division `a / b`
|
||||||
|
fn div(lhs, rhs) {
|
||||||
|
Ok(match (lhs, rhs){
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a / b),
|
||||||
|
_ => Err(Error::TypeError())?
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remainder `a % b`
|
||||||
|
fn rem(lhs, rhs) {
|
||||||
|
Ok(match (lhs, rhs) {
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a % b),
|
||||||
|
_ => Err(Error::TypeError())?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Addition `a + b`
|
||||||
|
fn add(lhs, rhs) {
|
||||||
|
Ok(match (lhs, rhs) {
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b),
|
||||||
|
(ConValue::Str(a), ConValue::Str(b)) => (a.to_string() + b).into(),
|
||||||
|
(ConValue::Str(a), ConValue::String(b)) => (a.to_string() + b).into(),
|
||||||
|
(ConValue::String(a), ConValue::Str(b)) => (a.to_string() + b).into(),
|
||||||
|
(ConValue::String(a), ConValue::String(b)) => (a.to_string() + b).into(),
|
||||||
|
(ConValue::Str(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(*c); s.into() }
|
||||||
|
(ConValue::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(*c); s.into() }
|
||||||
|
(ConValue::Char(a), ConValue::Char(b)) => {
|
||||||
|
ConValue::String([a, b].into_iter().collect::<String>())
|
||||||
|
}
|
||||||
|
_ => Err(Error::TypeError())?
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subtraction `a - b`
|
||||||
|
fn sub(lhs, rhs) {
|
||||||
|
Ok(match (lhs, rhs) {
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a - b),
|
||||||
|
_ => Err(Error::TypeError())?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shift Left `a << b`
|
||||||
|
fn shl(lhs, rhs) {
|
||||||
|
Ok(match (lhs, rhs) {
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a << b),
|
||||||
|
_ => Err(Error::TypeError())?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shift Right `a >> b`
|
||||||
|
fn shr(lhs, rhs) {
|
||||||
|
Ok(match (lhs, rhs) {
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a >> b),
|
||||||
|
_ => Err(Error::TypeError())?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bitwise And `a & b`
|
||||||
|
fn and(lhs, rhs) {
|
||||||
|
Ok(match (lhs, rhs) {
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
|
||||||
|
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
|
||||||
|
_ => Err(Error::TypeError())?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bitwise Or `a | b`
|
||||||
|
fn or(lhs, rhs) {
|
||||||
|
Ok(match (lhs, rhs) {
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
|
||||||
|
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
|
||||||
|
_ => Err(Error::TypeError())?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bitwise Exclusive Or `a ^ b`
|
||||||
|
fn xor(lhs, rhs) {
|
||||||
|
Ok(match (lhs, rhs) {
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
|
||||||
|
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
|
||||||
|
_ => Err(Error::TypeError())?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn RangeExc(start, end) @env {
|
||||||
|
Ok(ConValue::TupleStruct("RangeExc".into(), Box::new(Box::new([start.clone(), end.clone()]))))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn RangeInc(start, end) @env {
|
||||||
|
Ok(ConValue::TupleStruct("RangeInc".into(), Box::new(Box::new([start.clone(), end.clone()]))))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn RangeTo(end) @env {
|
||||||
|
Ok(ConValue::TupleStruct("RangeTo".into(), Box::new(Box::new([end.clone()]))))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn RangeToInc(end) @env {
|
||||||
|
Ok(ConValue::TupleStruct("RangeToInc".into(), Box::new(Box::new([end.clone()]))))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Negates the ConValue
|
||||||
|
fn neg(tail) {
|
||||||
|
Ok(match tail {
|
||||||
|
ConValue::Empty => ConValue::Empty,
|
||||||
|
ConValue::Int(v) => ConValue::Int(v.wrapping_neg()),
|
||||||
|
ConValue::Float(v) => ConValue::Float(-v),
|
||||||
|
_ => Err(Error::TypeError())?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inverts the ConValue
|
||||||
|
fn not(tail) {
|
||||||
|
Ok(match tail {
|
||||||
|
ConValue::Empty => ConValue::Empty,
|
||||||
|
ConValue::Int(v) => ConValue::Int(!v),
|
||||||
|
ConValue::Bool(v) => ConValue::Bool(!v),
|
||||||
|
_ => Err(Error::TypeError())?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compares two values
|
||||||
|
fn cmp(head, tail) {
|
||||||
|
Ok(ConValue::Int(match (head, tail) {
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => a.cmp(b) as _,
|
||||||
|
(ConValue::Bool(a), ConValue::Bool(b)) => a.cmp(b) as _,
|
||||||
|
(ConValue::Char(a), ConValue::Char(b)) => a.cmp(b) as _,
|
||||||
|
(ConValue::Str(a), ConValue::Str(b)) => a.cmp(b) as _,
|
||||||
|
(ConValue::Str(a), ConValue::String(b)) => a.to_ref().cmp(b.as_str()) as _,
|
||||||
|
(ConValue::String(a), ConValue::Str(b)) => a.as_str().cmp(b.to_ref()) as _,
|
||||||
|
(ConValue::String(a), ConValue::String(b)) => a.cmp(b) as _,
|
||||||
|
_ => Err(error_format!("Incomparable values: {head}, {tail}"))?
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Does the opposite of `&`
|
||||||
|
fn deref(tail) @env {
|
||||||
|
Ok(match tail {
|
||||||
|
ConValue::Ref(v) => env.get_id(*v).cloned().ok_or(Error::StackOverflow(*v))?,
|
||||||
|
_ => tail.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
];
|
||||||
68
compiler/cl-interpret/src/closure.rs
Normal file
68
compiler/cl-interpret/src/closure.rs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
use crate::{
|
||||||
|
Callable,
|
||||||
|
convalue::ConValue,
|
||||||
|
env::Environment,
|
||||||
|
error::{Error, ErrorKind, IResult},
|
||||||
|
function::collect_upvars::CollectUpvars,
|
||||||
|
interpret::Interpret,
|
||||||
|
pattern,
|
||||||
|
};
|
||||||
|
use cl_ast::{Sym, ast_visitor::Visit};
|
||||||
|
use std::{collections::HashMap, fmt::Display};
|
||||||
|
|
||||||
|
/// Represents an ad-hoc anonymous function
|
||||||
|
/// which captures surrounding state by COPY
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Closure {
|
||||||
|
decl: cl_ast::Closure,
|
||||||
|
lift: HashMap<Sym, ConValue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Closure {
|
||||||
|
const NAME: &'static str = "{closure}";
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Closure {
|
||||||
|
pub fn new(env: &mut Environment, decl: &cl_ast::Closure) -> Self {
|
||||||
|
let lift = CollectUpvars::new(env).visit(decl).finish_copied();
|
||||||
|
Self { decl: decl.clone(), lift }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Closure {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { decl, lift: _ } = self;
|
||||||
|
write!(f, "{decl}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Callable for Closure {
|
||||||
|
fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
|
||||||
|
let Self { decl, lift } = self;
|
||||||
|
let mut env = env.frame(Self::NAME);
|
||||||
|
|
||||||
|
// place lifts in scope
|
||||||
|
for (name, value) in lift.clone() {
|
||||||
|
env.insert(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut env = env.frame("args");
|
||||||
|
|
||||||
|
for (name, value) in pattern::substitution(&env, &decl.arg, ConValue::Tuple(args.into()))? {
|
||||||
|
env.insert(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = decl.body.interpret(&mut env);
|
||||||
|
drop(env);
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Err(Error { kind: ErrorKind::Return(value), .. }) => Ok(value),
|
||||||
|
Err(Error { kind: ErrorKind::Break(value), .. }) => Err(Error::BadBreak(value)),
|
||||||
|
other => other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> cl_ast::Sym {
|
||||||
|
Self::NAME.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
449
compiler/cl-interpret/src/convalue.rs
Normal file
449
compiler/cl-interpret/src/convalue.rs
Normal file
@@ -0,0 +1,449 @@
|
|||||||
|
//! Values in the dynamically typed AST interpreter.
|
||||||
|
//!
|
||||||
|
//! The most permanent fix is a temporary one.
|
||||||
|
use cl_ast::{Expr, Sym, format::FmtAdapter};
|
||||||
|
|
||||||
|
use crate::{closure::Closure, constructor::Constructor};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
Callable, Environment,
|
||||||
|
builtin::Builtin,
|
||||||
|
error::{Error, IResult},
|
||||||
|
function::Function,
|
||||||
|
};
|
||||||
|
use std::{collections::HashMap, ops::*, rc::Rc};
|
||||||
|
|
||||||
|
/*
|
||||||
|
A Value can be:
|
||||||
|
- A Primitive (Empty, isize, etc.)
|
||||||
|
- A Record (Array, Tuple, Struct)
|
||||||
|
- A Variant (discriminant, Value) pair
|
||||||
|
|
||||||
|
array [
|
||||||
|
10, // 0
|
||||||
|
20, // 1
|
||||||
|
]
|
||||||
|
|
||||||
|
tuple (
|
||||||
|
10, // 0
|
||||||
|
20, // 1
|
||||||
|
)
|
||||||
|
|
||||||
|
struct {
|
||||||
|
x: 10, // x => 0
|
||||||
|
y: 20, // y => 1
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
type Integer = isize;
|
||||||
|
|
||||||
|
/// A Conlang value stores data in the interpreter
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub enum ConValue {
|
||||||
|
/// The empty/unit `()` type
|
||||||
|
#[default]
|
||||||
|
Empty,
|
||||||
|
/// An integer
|
||||||
|
Int(Integer),
|
||||||
|
/// A floating point number
|
||||||
|
Float(f64),
|
||||||
|
/// A boolean
|
||||||
|
Bool(bool),
|
||||||
|
/// A unicode character
|
||||||
|
Char(char),
|
||||||
|
/// A string literal
|
||||||
|
Str(Sym),
|
||||||
|
/// A dynamic string
|
||||||
|
String(String),
|
||||||
|
/// A reference
|
||||||
|
Ref(usize),
|
||||||
|
/// A reference to an array
|
||||||
|
Slice(usize, usize),
|
||||||
|
/// An Array
|
||||||
|
Array(Box<[ConValue]>),
|
||||||
|
/// A tuple
|
||||||
|
Tuple(Box<[ConValue]>),
|
||||||
|
// TODO: Instead of storing the identifier, store the index of the struct module
|
||||||
|
/// A value of a product type
|
||||||
|
Struct(Sym, Box<HashMap<Sym, ConValue>>),
|
||||||
|
/// A value of a product type with anonymous members
|
||||||
|
TupleStruct(Sym, Box<Box<[ConValue]>>),
|
||||||
|
/// An entire namespace
|
||||||
|
Module(Box<HashMap<Sym, ConValue>>),
|
||||||
|
/// A quoted expression
|
||||||
|
Quote(Rc<Expr>),
|
||||||
|
/// A callable thing
|
||||||
|
Function(Rc<Function>),
|
||||||
|
/// A tuple constructor
|
||||||
|
TupleConstructor(Constructor),
|
||||||
|
/// A closure, capturing by reference
|
||||||
|
Closure(Rc<Closure>),
|
||||||
|
/// A built-in function
|
||||||
|
Builtin(&'static Builtin),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConValue {
|
||||||
|
/// Gets whether the current value is true or false
|
||||||
|
pub fn truthy(&self) -> IResult<bool> {
|
||||||
|
match self {
|
||||||
|
ConValue::Bool(v) => Ok(*v),
|
||||||
|
_ => Err(Error::TypeError())?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn typename(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
ConValue::Empty => "Empty",
|
||||||
|
ConValue::Int(_) => "i64",
|
||||||
|
ConValue::Float(_) => "f64",
|
||||||
|
ConValue::Bool(_) => "bool",
|
||||||
|
ConValue::Char(_) => "char",
|
||||||
|
ConValue::Str(_) => "str",
|
||||||
|
ConValue::String(_) => "String",
|
||||||
|
ConValue::Ref(_) => "Ref",
|
||||||
|
ConValue::Slice(_, _) => "Slice",
|
||||||
|
ConValue::Array(_) => "Array",
|
||||||
|
ConValue::Tuple(_) => "Tuple",
|
||||||
|
ConValue::Struct(_, _) => "Struct",
|
||||||
|
ConValue::TupleStruct(_, _) => "TupleStruct",
|
||||||
|
ConValue::Module(_) => "",
|
||||||
|
ConValue::Quote(_) => "Quote",
|
||||||
|
ConValue::Function(_) => "Fn",
|
||||||
|
ConValue::TupleConstructor(_) => "Fn",
|
||||||
|
ConValue::Closure(_) => "Fn",
|
||||||
|
ConValue::Builtin(_) => "Fn",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn TupleStruct(id: Sym, values: Box<[ConValue]>) -> Self {
|
||||||
|
Self::TupleStruct(id, Box::new(values))
|
||||||
|
}
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn Struct(id: Sym, values: HashMap<Sym, ConValue>) -> Self {
|
||||||
|
Self::Struct(id, Box::new(values))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn index(&self, index: &Self, _env: &Environment) -> IResult<ConValue> {
|
||||||
|
let &Self::Int(index) = index else {
|
||||||
|
Err(Error::TypeError())?
|
||||||
|
};
|
||||||
|
match self {
|
||||||
|
ConValue::Str(string) => string
|
||||||
|
.chars()
|
||||||
|
.nth(index as _)
|
||||||
|
.map(ConValue::Char)
|
||||||
|
.ok_or(Error::OobIndex(index as usize, string.chars().count())),
|
||||||
|
ConValue::String(string) => string
|
||||||
|
.chars()
|
||||||
|
.nth(index as _)
|
||||||
|
.map(ConValue::Char)
|
||||||
|
.ok_or(Error::OobIndex(index as usize, string.chars().count())),
|
||||||
|
ConValue::Array(arr) => arr
|
||||||
|
.get(index as usize)
|
||||||
|
.cloned()
|
||||||
|
.ok_or(Error::OobIndex(index as usize, arr.len())),
|
||||||
|
&ConValue::Slice(id, len) => {
|
||||||
|
let index = if index < 0 {
|
||||||
|
len.wrapping_add_signed(index)
|
||||||
|
} else {
|
||||||
|
index as usize
|
||||||
|
};
|
||||||
|
if index < len {
|
||||||
|
Ok(ConValue::Ref(id + index))
|
||||||
|
} else {
|
||||||
|
Err(Error::OobIndex(index, len))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(Error::TypeError()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmp! {
|
||||||
|
lt: false, <;
|
||||||
|
lt_eq: true, <=;
|
||||||
|
eq: true, ==;
|
||||||
|
neq: false, !=;
|
||||||
|
gt_eq: true, >=;
|
||||||
|
gt: false, >;
|
||||||
|
}
|
||||||
|
assign! {
|
||||||
|
add_assign: +;
|
||||||
|
bitand_assign: &;
|
||||||
|
bitor_assign: |;
|
||||||
|
bitxor_assign: ^;
|
||||||
|
div_assign: /;
|
||||||
|
mul_assign: *;
|
||||||
|
rem_assign: %;
|
||||||
|
shl_assign: <<;
|
||||||
|
shr_assign: >>;
|
||||||
|
sub_assign: -;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Callable for ConValue {
|
||||||
|
fn name(&self) -> Sym {
|
||||||
|
match self {
|
||||||
|
ConValue::Function(func) => func.name(),
|
||||||
|
ConValue::Closure(func) => func.name(),
|
||||||
|
ConValue::Builtin(func) => func.name(),
|
||||||
|
_ => "".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
|
||||||
|
match self {
|
||||||
|
Self::Function(func) => func.call(env, args),
|
||||||
|
Self::TupleConstructor(func) => func.call(env, args),
|
||||||
|
Self::Closure(func) => func.call(env, args),
|
||||||
|
Self::Builtin(func) => func.call(env, args),
|
||||||
|
Self::Module(m) => {
|
||||||
|
if let Some(func) = m.get(&"call".into()) {
|
||||||
|
func.call(env, args)
|
||||||
|
} else {
|
||||||
|
Err(Error::NotCallable(self.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&Self::Ref(ptr) => {
|
||||||
|
// Move onto stack, and call
|
||||||
|
let func = env.get_id(ptr).ok_or(Error::StackOverflow(ptr))?.clone();
|
||||||
|
func.call(env, args)
|
||||||
|
}
|
||||||
|
_ => Err(Error::NotCallable(self.clone())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Templates comparison functions for [ConValue]
|
||||||
|
macro cmp ($($fn:ident: $empty:literal, $op:tt);*$(;)?) {$(
|
||||||
|
/// TODO: Remove when functions are implemented:
|
||||||
|
/// Desugar into function calls
|
||||||
|
pub fn $fn(&self, other: &Self) -> IResult<Self> {
|
||||||
|
match (self, other) {
|
||||||
|
(Self::Empty, Self::Empty) => Ok(Self::Bool($empty)),
|
||||||
|
(Self::Int(a), Self::Int(b)) => Ok(Self::Bool(a $op b)),
|
||||||
|
(Self::Float(a), Self::Float(b)) => Ok(Self::Bool(a $op b)),
|
||||||
|
(Self::Bool(a), Self::Bool(b)) => Ok(Self::Bool(a $op b)),
|
||||||
|
(Self::Char(a), Self::Char(b)) => Ok(Self::Bool(a $op b)),
|
||||||
|
(Self::Str(a), Self::Str(b)) => Ok(Self::Bool(&**a $op &**b)),
|
||||||
|
(Self::Str(a), Self::String(b)) => Ok(Self::Bool(&**a $op &**b)),
|
||||||
|
(Self::String(a), Self::Str(b)) => Ok(Self::Bool(&**a $op &**b)),
|
||||||
|
(Self::String(a), Self::String(b)) => Ok(Self::Bool(&**a $op &**b)),
|
||||||
|
_ => Err(Error::TypeError())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*}
|
||||||
|
macro assign($( $fn: ident: $op: tt );*$(;)?) {$(
|
||||||
|
pub fn $fn(&mut self, other: Self) -> IResult<()> {
|
||||||
|
*self = (std::mem::take(self) $op other)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
)*}
|
||||||
|
/// Implements [From] for an enum with 1-tuple variants
|
||||||
|
macro from ($($T:ty => $v:expr),*$(,)?) {
|
||||||
|
$(impl From<$T> for ConValue {
|
||||||
|
fn from(value: $T) -> Self { $v(value.into()) }
|
||||||
|
})*
|
||||||
|
}
|
||||||
|
impl From<&Sym> for ConValue {
|
||||||
|
fn from(value: &Sym) -> Self {
|
||||||
|
ConValue::Str(*value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
from! {
|
||||||
|
Integer => ConValue::Int,
|
||||||
|
f64 => ConValue::Float,
|
||||||
|
bool => ConValue::Bool,
|
||||||
|
char => ConValue::Char,
|
||||||
|
Sym => ConValue::Str,
|
||||||
|
&str => ConValue::Str,
|
||||||
|
Expr => ConValue::Quote,
|
||||||
|
String => ConValue::String,
|
||||||
|
Rc<str> => ConValue::Str,
|
||||||
|
Function => ConValue::Function,
|
||||||
|
Vec<ConValue> => ConValue::Tuple,
|
||||||
|
&'static Builtin => ConValue::Builtin,
|
||||||
|
}
|
||||||
|
impl From<()> for ConValue {
|
||||||
|
fn from(_: ()) -> Self {
|
||||||
|
Self::Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&[ConValue]> for ConValue {
|
||||||
|
fn from(value: &[ConValue]) -> Self {
|
||||||
|
match value {
|
||||||
|
[] => Self::Empty,
|
||||||
|
[value] => value.clone(),
|
||||||
|
_ => Self::Tuple(value.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements binary [std::ops] traits for [ConValue]
|
||||||
|
///
|
||||||
|
/// TODO: Desugar operators into function calls
|
||||||
|
macro ops($($trait:ty: $fn:ident = [$($match:tt)*])*) {
|
||||||
|
$(impl $trait for ConValue {
|
||||||
|
type Output = IResult<Self>;
|
||||||
|
/// TODO: Desugar operators into function calls
|
||||||
|
fn $fn(self, rhs: Self) -> Self::Output {Ok(match (self, rhs) {$($match)*})}
|
||||||
|
})*
|
||||||
|
}
|
||||||
|
ops! {
|
||||||
|
Add: add = [
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_add(b)),
|
||||||
|
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a + b),
|
||||||
|
(ConValue::Str(a), ConValue::Str(b)) => (a.to_string() + &*b).into(),
|
||||||
|
(ConValue::Str(a), ConValue::String(b)) => (a.to_string() + &*b).into(),
|
||||||
|
(ConValue::String(a), ConValue::Str(b)) => (a.to_string() + &*b).into(),
|
||||||
|
(ConValue::String(a), ConValue::String(b)) => (a.to_string() + &*b).into(),
|
||||||
|
(ConValue::Str(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(c); s.into() }
|
||||||
|
(ConValue::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(c); s.into() }
|
||||||
|
(ConValue::Char(a), ConValue::Char(b)) => {
|
||||||
|
ConValue::String([a, b].into_iter().collect::<String>())
|
||||||
|
}
|
||||||
|
_ => Err(Error::TypeError())?
|
||||||
|
]
|
||||||
|
BitAnd: bitand = [
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
|
||||||
|
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
|
||||||
|
_ => Err(Error::TypeError())?
|
||||||
|
]
|
||||||
|
BitOr: bitor = [
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
|
||||||
|
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
|
||||||
|
_ => Err(Error::TypeError())?
|
||||||
|
]
|
||||||
|
BitXor: bitxor = [
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
|
||||||
|
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
|
||||||
|
_ => Err(Error::TypeError())?
|
||||||
|
]
|
||||||
|
Div: div = [
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_div(b).unwrap_or_else(|| {
|
||||||
|
eprintln!("Warning: Divide by zero in {a} / {b}"); a
|
||||||
|
})),
|
||||||
|
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a / b),
|
||||||
|
_ => Err(Error::TypeError())?
|
||||||
|
]
|
||||||
|
Mul: mul = [
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_mul(b)),
|
||||||
|
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a * b),
|
||||||
|
_ => Err(Error::TypeError())?
|
||||||
|
]
|
||||||
|
Rem: rem = [
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_rem(b).unwrap_or_else(|| {
|
||||||
|
println!("Warning: Divide by zero in {a} % {b}"); a
|
||||||
|
})),
|
||||||
|
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a % b),
|
||||||
|
_ => Err(Error::TypeError())?
|
||||||
|
]
|
||||||
|
Shl: shl = [
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shl(b as _)),
|
||||||
|
_ => Err(Error::TypeError())?
|
||||||
|
]
|
||||||
|
Shr: shr = [
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shr(b as _)),
|
||||||
|
_ => Err(Error::TypeError())?
|
||||||
|
]
|
||||||
|
Sub: sub = [
|
||||||
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
||||||
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_sub(b)),
|
||||||
|
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a - b),
|
||||||
|
_ => Err(Error::TypeError())?
|
||||||
|
]
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for ConValue {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
ConValue::Empty => "Empty".fmt(f),
|
||||||
|
ConValue::Int(v) => v.fmt(f),
|
||||||
|
ConValue::Float(v) => v.fmt(f),
|
||||||
|
ConValue::Bool(v) => v.fmt(f),
|
||||||
|
ConValue::Char(v) => v.fmt(f),
|
||||||
|
ConValue::Str(v) => v.fmt(f),
|
||||||
|
ConValue::String(v) => v.fmt(f),
|
||||||
|
ConValue::Ref(v) => write!(f, "&<{}>", v),
|
||||||
|
ConValue::Slice(id, len) => write!(f, "&<{id}>[{len}..]"),
|
||||||
|
ConValue::Array(array) => {
|
||||||
|
'['.fmt(f)?;
|
||||||
|
for (idx, element) in array.iter().enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
", ".fmt(f)?
|
||||||
|
}
|
||||||
|
element.fmt(f)?
|
||||||
|
}
|
||||||
|
']'.fmt(f)
|
||||||
|
}
|
||||||
|
ConValue::Tuple(tuple) => {
|
||||||
|
'('.fmt(f)?;
|
||||||
|
for (idx, element) in tuple.iter().enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
", ".fmt(f)?
|
||||||
|
}
|
||||||
|
element.fmt(f)?
|
||||||
|
}
|
||||||
|
')'.fmt(f)
|
||||||
|
}
|
||||||
|
ConValue::TupleStruct(id, tuple) => {
|
||||||
|
write!(f, "{id}")?;
|
||||||
|
'('.fmt(f)?;
|
||||||
|
for (idx, element) in tuple.iter().enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
", ".fmt(f)?
|
||||||
|
}
|
||||||
|
element.fmt(f)?
|
||||||
|
}
|
||||||
|
')'.fmt(f)
|
||||||
|
}
|
||||||
|
ConValue::Struct(id, map) => {
|
||||||
|
use std::fmt::Write;
|
||||||
|
write!(f, "{id} ")?;
|
||||||
|
let mut f = f.delimit_with("{", "\n}");
|
||||||
|
for (k, v) in map.iter() {
|
||||||
|
write!(f, "\n{k}: {v},")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
ConValue::Module(module) => {
|
||||||
|
use std::fmt::Write;
|
||||||
|
let mut f = f.delimit_with("{", "\n}");
|
||||||
|
for (k, v) in module.iter() {
|
||||||
|
write!(f, "\n{k}: {v},")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
ConValue::Quote(q) => {
|
||||||
|
write!(f, "`{q}`")
|
||||||
|
}
|
||||||
|
ConValue::Function(func) => {
|
||||||
|
write!(f, "{}", func.decl())
|
||||||
|
}
|
||||||
|
ConValue::TupleConstructor(Constructor { name: index, arity }) => {
|
||||||
|
write!(f, "{index}(..{arity})")
|
||||||
|
}
|
||||||
|
ConValue::Closure(func) => {
|
||||||
|
write!(f, "{}", func.as_ref())
|
||||||
|
}
|
||||||
|
ConValue::Builtin(func) => {
|
||||||
|
write!(f, "{}", func)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub macro cvstruct (
|
||||||
|
$Name:ident {
|
||||||
|
$($member:ident : $expr:expr),*
|
||||||
|
}
|
||||||
|
) {{
|
||||||
|
let mut members = HashMap::new();
|
||||||
|
$(members.insert(stringify!($member).into(), ($expr).into());)*
|
||||||
|
ConValue::Struct(Box::new((stringify!($Name).into(), members)))
|
||||||
|
}}
|
||||||
310
compiler/cl-interpret/src/env.rs
Normal file
310
compiler/cl-interpret/src/env.rs
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
//! Lexical and non-lexical scoping for variables
|
||||||
|
|
||||||
|
use crate::{builtin::Builtin, constructor::Constructor, modules::ModuleTree};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
Callable, Interpret,
|
||||||
|
builtin::{Builtins, Math},
|
||||||
|
convalue::ConValue,
|
||||||
|
error::{Error, IResult},
|
||||||
|
function::Function,
|
||||||
|
};
|
||||||
|
use cl_ast::{Function as FnDecl, Sym};
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
fmt::Display,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type StackFrame = HashMap<Sym, ConValue>;
|
||||||
|
|
||||||
|
pub type StackBinds = HashMap<Sym, usize>;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub(crate) struct EnvFrame {
|
||||||
|
pub name: Option<&'static str>,
|
||||||
|
/// The length of the array when this stack frame was constructed
|
||||||
|
pub base: usize,
|
||||||
|
/// The bindings of name to stack position
|
||||||
|
pub binds: StackBinds,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements a nested lexical scope
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Environment {
|
||||||
|
values: Vec<ConValue>,
|
||||||
|
frames: Vec<EnvFrame>,
|
||||||
|
modules: ModuleTree,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Environment {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
for EnvFrame { name, base: _, binds } in self.frames.iter().rev() {
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"--- {}[{}] ---",
|
||||||
|
if let Some(name) = name { name } else { "" },
|
||||||
|
binds.len(),
|
||||||
|
)?;
|
||||||
|
let mut binds: Vec<_> = binds.iter().collect();
|
||||||
|
binds.sort_by(|(_, a), (_, b)| a.cmp(b));
|
||||||
|
for (name, idx) in binds {
|
||||||
|
write!(f, "{idx:4} {name}: ")?;
|
||||||
|
match self.values.get(*idx) {
|
||||||
|
Some(value) => writeln!(f, "\t{value}"),
|
||||||
|
None => writeln!(f, "ERROR: {name}'s address blows the stack!"),
|
||||||
|
}?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Environment {
|
||||||
|
fn default() -> Self {
|
||||||
|
let mut this = Self::no_builtins();
|
||||||
|
this.add_builtins(Builtins).add_builtins(Math);
|
||||||
|
this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Environment {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
/// Creates an [Environment] with no [builtins](super::builtin)
|
||||||
|
pub fn no_builtins() -> Self {
|
||||||
|
Self {
|
||||||
|
values: Vec::new(),
|
||||||
|
frames: vec![EnvFrame::default()],
|
||||||
|
modules: ModuleTree::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reflexively evaluates a node
|
||||||
|
pub fn eval(&mut self, node: &impl Interpret) -> IResult<ConValue> {
|
||||||
|
node.interpret(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls a function inside the Environment's scope,
|
||||||
|
/// and returns the result
|
||||||
|
pub fn call(&mut self, name: Sym, args: &[ConValue]) -> IResult<ConValue> {
|
||||||
|
let function = self.get(name)?;
|
||||||
|
function.call(self, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn modules_mut(&mut self) -> &mut ModuleTree {
|
||||||
|
&mut self.modules
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn modules(&self) -> &ModuleTree {
|
||||||
|
&self.modules
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Binds a value to the given name in the current scope.
|
||||||
|
pub fn bind(&mut self, name: impl Into<Sym>, value: impl Into<ConValue>) {
|
||||||
|
self.insert(name.into(), value.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bind_raw(&mut self, name: Sym, id: usize) -> Option<()> {
|
||||||
|
let EnvFrame { name: _, base: _, binds } = self.frames.last_mut()?;
|
||||||
|
binds.insert(name, id);
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets all registered globals, bound or unbound.
|
||||||
|
pub(crate) fn globals(&self) -> &EnvFrame {
|
||||||
|
self.frames.first().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds builtins
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Will panic if stack contains more than the globals frame!
|
||||||
|
pub fn add_builtins(&mut self, builtins: &'static [Builtin]) -> &mut Self {
|
||||||
|
if self.frames.len() != 1 {
|
||||||
|
panic!("Cannot add builtins to full stack: {self}")
|
||||||
|
}
|
||||||
|
|
||||||
|
for builtin in builtins {
|
||||||
|
self.insert(builtin.name(), builtin.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_frame(&mut self, name: &'static str, frame: StackFrame) {
|
||||||
|
self.frames.push(EnvFrame {
|
||||||
|
name: Some(name),
|
||||||
|
base: self.values.len(),
|
||||||
|
binds: HashMap::new(),
|
||||||
|
});
|
||||||
|
for (k, v) in frame {
|
||||||
|
self.insert(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_frame(&mut self) -> Option<(StackFrame, &'static str)> {
|
||||||
|
let mut out = HashMap::new();
|
||||||
|
let EnvFrame { name, base, binds } = self.frames.pop()?;
|
||||||
|
for (k, v) in binds {
|
||||||
|
out.insert(k, self.values.get_mut(v).map(std::mem::take)?);
|
||||||
|
}
|
||||||
|
self.values.truncate(base);
|
||||||
|
Some((out, name.unwrap_or("")))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enters a nested scope, returning a [`Frame`] stack-guard.
|
||||||
|
///
|
||||||
|
/// [`Frame`] implements Deref/DerefMut for [`Environment`].
|
||||||
|
pub fn frame(&mut self, name: &'static str) -> Frame<'_> {
|
||||||
|
Frame::new(self, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enters a nested scope, assigning the contents of `frame`,
|
||||||
|
/// and returning a [`Frame`] stack-guard.
|
||||||
|
///
|
||||||
|
/// [`Frame`] implements Deref/DerefMut for [`Environment`].
|
||||||
|
pub fn with_frame<'e>(&'e mut self, name: &'static str, frame: StackFrame) -> Frame<'e> {
|
||||||
|
let mut scope = self.frame(name);
|
||||||
|
for (k, v) in frame {
|
||||||
|
scope.insert(k, v);
|
||||||
|
}
|
||||||
|
scope
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolves a variable mutably.
|
||||||
|
///
|
||||||
|
/// Returns a mutable reference to the variable's record, if it exists.
|
||||||
|
pub fn get_mut(&mut self, name: Sym) -> IResult<&mut ConValue> {
|
||||||
|
let at = self.id_of(name)?;
|
||||||
|
self.get_id_mut(at).ok_or(Error::NotDefined(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolves a variable immutably.
|
||||||
|
///
|
||||||
|
/// Returns a reference to the variable's contents, if it is defined and initialized.
|
||||||
|
pub fn get(&self, name: Sym) -> IResult<ConValue> {
|
||||||
|
let id = self.id_of(name)?;
|
||||||
|
let res = self.values.get(id);
|
||||||
|
Ok(res.ok_or(Error::NotDefined(name))?.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolves the index associated with a [Sym]
|
||||||
|
pub fn id_of(&self, name: Sym) -> IResult<usize> {
|
||||||
|
for EnvFrame { binds, .. } in self.frames.iter().rev() {
|
||||||
|
if let Some(id) = binds.get(&name).copied() {
|
||||||
|
return Ok(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(Error::NotDefined(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_id(&self, id: usize) -> Option<&ConValue> {
|
||||||
|
self.values.get(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_id_mut(&mut self, id: usize) -> Option<&mut ConValue> {
|
||||||
|
self.values.get_mut(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_slice(&self, start: usize, len: usize) -> Option<&[ConValue]> {
|
||||||
|
self.values.get(start..start + len)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_slice_mut(&mut self, start: usize, len: usize) -> Option<&mut [ConValue]> {
|
||||||
|
self.values.get_mut(start..start + len)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts a new [ConValue] into this [Environment]
|
||||||
|
pub fn insert(&mut self, k: Sym, v: ConValue) {
|
||||||
|
if self.bind_raw(k, self.values.len()).is_some() {
|
||||||
|
self.values.push(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A convenience function for registering a [FnDecl] as a [Function]
|
||||||
|
pub fn insert_fn(&mut self, decl: &FnDecl) {
|
||||||
|
let FnDecl { name, .. } = decl;
|
||||||
|
let (name, function) = (*name, Rc::new(Function::new(decl)));
|
||||||
|
self.insert(name, ConValue::Function(function.clone()));
|
||||||
|
// Tell the function to lift its upvars now, after it's been declared
|
||||||
|
function.lift_upvars(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_tup_constructor(&mut self, name: Sym, arity: usize) {
|
||||||
|
let cs = Constructor { arity: arity as _, name };
|
||||||
|
self.insert(name, ConValue::TupleConstructor(cs));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the current stack top position
|
||||||
|
pub fn pos(&self) -> usize {
|
||||||
|
self.values.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocates a local variable
|
||||||
|
pub fn stack_alloc(&mut self, value: ConValue) -> IResult<usize> {
|
||||||
|
let adr = self.values.len();
|
||||||
|
self.values.push(value);
|
||||||
|
Ok(adr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocates some space on the stack
|
||||||
|
pub fn alloca(&mut self, value: ConValue, len: usize) -> ConValue {
|
||||||
|
let idx = self.values.len();
|
||||||
|
self.values.extend(std::iter::repeat_n(value, len));
|
||||||
|
ConValue::Slice(idx, len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a stack frame
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Frame<'scope> {
|
||||||
|
scope: &'scope mut Environment,
|
||||||
|
}
|
||||||
|
impl<'scope> Frame<'scope> {
|
||||||
|
fn new(scope: &'scope mut Environment, name: &'static str) -> Self {
|
||||||
|
scope.frames.push(EnvFrame {
|
||||||
|
name: Some(name),
|
||||||
|
base: scope.values.len(),
|
||||||
|
binds: HashMap::new(),
|
||||||
|
});
|
||||||
|
|
||||||
|
Self { scope }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_values(mut self) -> Option<StackFrame> {
|
||||||
|
let mut out = HashMap::new();
|
||||||
|
let binds = std::mem::take(&mut self.frames.last_mut()?.binds);
|
||||||
|
for (k, v) in binds {
|
||||||
|
out.insert(k, self.values.get_mut(v).map(std::mem::take)?);
|
||||||
|
}
|
||||||
|
Some(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_binds(mut self) -> Option<StackBinds> {
|
||||||
|
let EnvFrame { name: _, base: _, binds } = self.frames.pop()?;
|
||||||
|
std::mem::forget(self);
|
||||||
|
Some(binds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Deref for Frame<'_> {
|
||||||
|
type Target = Environment;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.scope
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl DerefMut for Frame<'_> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
self.scope
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Drop for Frame<'_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(frame) = self.frames.pop() {
|
||||||
|
self.values.truncate(frame.base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
218
compiler/cl-interpret/src/error.rs
Normal file
218
compiler/cl-interpret/src/error.rs
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
//! The [Error] type represents any error thrown by the [Environment](super::Environment)
|
||||||
|
|
||||||
|
use cl_ast::{Pattern, Sym};
|
||||||
|
use cl_structures::span::Span;
|
||||||
|
|
||||||
|
use super::convalue::ConValue;
|
||||||
|
|
||||||
|
pub type IResult<T> = Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Error {
|
||||||
|
pub kind: ErrorKind,
|
||||||
|
pub(super) span: Option<Span>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
/// Adds a [struct Span] to this [Error], if there isn't already a more specific one.
|
||||||
|
pub fn with_span(self, span: Span) -> Self {
|
||||||
|
Self { span: self.span.or(Some(span)), ..self }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn kind(&self) -> &ErrorKind {
|
||||||
|
&self.kind
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Propagate a Return value
|
||||||
|
pub fn Return(value: ConValue) -> Self {
|
||||||
|
Self { kind: ErrorKind::Return(value), span: None }
|
||||||
|
}
|
||||||
|
/// Propagate a Break value
|
||||||
|
pub fn Break(value: ConValue) -> Self {
|
||||||
|
Self { kind: ErrorKind::Break(value), span: None }
|
||||||
|
}
|
||||||
|
/// Break propagated across function bounds
|
||||||
|
pub fn BadBreak(value: ConValue) -> Self {
|
||||||
|
Self { kind: ErrorKind::BadBreak(value), span: None }
|
||||||
|
}
|
||||||
|
/// Continue to the next iteration of a loop
|
||||||
|
pub fn Continue() -> Self {
|
||||||
|
Self { kind: ErrorKind::Continue, span: None }
|
||||||
|
}
|
||||||
|
/// Underflowed the stack
|
||||||
|
pub fn StackUnderflow() -> Self {
|
||||||
|
Self { kind: ErrorKind::StackUnderflow, span: None }
|
||||||
|
}
|
||||||
|
/// Overflowed the stack
|
||||||
|
pub fn StackOverflow(place: usize) -> Self {
|
||||||
|
Self { kind: ErrorKind::StackOverflow(place), span: None }
|
||||||
|
}
|
||||||
|
/// Exited the last scope
|
||||||
|
pub fn ScopeExit() -> Self {
|
||||||
|
Self { kind: ErrorKind::ScopeExit, span: None }
|
||||||
|
}
|
||||||
|
/// Type incompatibility
|
||||||
|
// TODO: store the type information in this error
|
||||||
|
pub fn TypeError() -> Self {
|
||||||
|
Self { kind: ErrorKind::TypeError, span: None }
|
||||||
|
}
|
||||||
|
/// In clause of For loop didn't yield a Range
|
||||||
|
pub fn NotIterable() -> Self {
|
||||||
|
Self { kind: ErrorKind::NotIterable, span: None }
|
||||||
|
}
|
||||||
|
/// A value could not be indexed
|
||||||
|
pub fn NotIndexable() -> Self {
|
||||||
|
Self { kind: ErrorKind::NotIndexable, span: None }
|
||||||
|
}
|
||||||
|
/// An array index went out of bounds
|
||||||
|
pub fn OobIndex(index: usize, length: usize) -> Self {
|
||||||
|
Self { kind: ErrorKind::OobIndex(index, length), span: None }
|
||||||
|
}
|
||||||
|
/// An expression is not assignable
|
||||||
|
pub fn NotAssignable() -> Self {
|
||||||
|
Self { kind: ErrorKind::NotAssignable, span: None }
|
||||||
|
}
|
||||||
|
/// A name was not defined in scope before being used
|
||||||
|
pub fn NotDefined(name: Sym) -> Self {
|
||||||
|
Self { kind: ErrorKind::NotDefined(name), span: None }
|
||||||
|
}
|
||||||
|
/// A name was defined but not initialized
|
||||||
|
pub fn NotInitialized(name: Sym) -> Self {
|
||||||
|
Self { kind: ErrorKind::NotInitialized(name), span: None }
|
||||||
|
}
|
||||||
|
/// A value was called, but is not callable
|
||||||
|
pub fn NotCallable(value: ConValue) -> Self {
|
||||||
|
Self { kind: ErrorKind::NotCallable(value), span: None }
|
||||||
|
}
|
||||||
|
/// A function was called with the wrong number of arguments
|
||||||
|
pub fn ArgNumber(want: usize, got: usize) -> Self {
|
||||||
|
Self { kind: ErrorKind::ArgNumber { want, got }, span: None }
|
||||||
|
}
|
||||||
|
/// A pattern failed to match
|
||||||
|
pub fn PatFailed(pat: Box<Pattern>) -> Self {
|
||||||
|
Self { kind: ErrorKind::PatFailed(pat), span: None }
|
||||||
|
}
|
||||||
|
/// Fell through a non-exhaustive match
|
||||||
|
pub fn MatchNonexhaustive() -> Self {
|
||||||
|
Self { kind: ErrorKind::MatchNonexhaustive, span: None }
|
||||||
|
}
|
||||||
|
/// Explicit panic
|
||||||
|
pub fn Panic(msg: String) -> Self {
|
||||||
|
Self { kind: ErrorKind::Panic(msg, 0), span: None }
|
||||||
|
}
|
||||||
|
/// Error produced by a Builtin
|
||||||
|
pub fn BuiltinError(msg: String) -> Self {
|
||||||
|
Self { kind: ErrorKind::BuiltinError(msg), span: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error {}
|
||||||
|
impl std::fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { kind, span } = self;
|
||||||
|
if let Some(Span { head, tail }) = span {
|
||||||
|
write!(f, "{head}..{tail}: ")?;
|
||||||
|
}
|
||||||
|
write!(f, "{kind}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents any error thrown by the [Environment](super::Environment)
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum ErrorKind {
|
||||||
|
/// Propagate a Return value
|
||||||
|
Return(ConValue),
|
||||||
|
/// Propagate a Break value
|
||||||
|
Break(ConValue),
|
||||||
|
/// Break propagated across function bounds
|
||||||
|
BadBreak(ConValue),
|
||||||
|
/// Continue to the next iteration of a loop
|
||||||
|
Continue,
|
||||||
|
/// Underflowed the stack
|
||||||
|
StackUnderflow,
|
||||||
|
/// Overflowed the stack
|
||||||
|
StackOverflow(usize),
|
||||||
|
/// Exited the last scope
|
||||||
|
ScopeExit,
|
||||||
|
/// Type incompatibility
|
||||||
|
// TODO: store the type information in this error
|
||||||
|
TypeError,
|
||||||
|
/// In clause of For loop didn't yield a Range
|
||||||
|
NotIterable,
|
||||||
|
/// A value could not be indexed
|
||||||
|
NotIndexable,
|
||||||
|
/// An array index went out of bounds
|
||||||
|
OobIndex(usize, usize),
|
||||||
|
/// An expression is not assignable
|
||||||
|
NotAssignable,
|
||||||
|
/// A name was not defined in scope before being used
|
||||||
|
NotDefined(Sym),
|
||||||
|
/// A name was defined but not initialized
|
||||||
|
NotInitialized(Sym),
|
||||||
|
/// A value was called, but is not callable
|
||||||
|
NotCallable(ConValue),
|
||||||
|
/// A function was called with the wrong number of arguments
|
||||||
|
ArgNumber { want: usize, got: usize },
|
||||||
|
/// A pattern failed to match
|
||||||
|
PatFailed(Box<Pattern>),
|
||||||
|
/// Fell through a non-exhaustive match
|
||||||
|
MatchNonexhaustive,
|
||||||
|
/// Explicit panic
|
||||||
|
Panic(String, usize),
|
||||||
|
/// Error produced by a Builtin
|
||||||
|
BuiltinError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for ErrorKind {}
|
||||||
|
impl std::fmt::Display for ErrorKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
ErrorKind::Return(value) => write!(f, "return {value}"),
|
||||||
|
ErrorKind::Break(value) => write!(f, "break {value}"),
|
||||||
|
ErrorKind::BadBreak(value) => write!(f, "rogue break: {value}"),
|
||||||
|
ErrorKind::Continue => "continue".fmt(f),
|
||||||
|
ErrorKind::StackUnderflow => "Stack underflow".fmt(f),
|
||||||
|
ErrorKind::StackOverflow(id) => {
|
||||||
|
write!(f, "Attempt to access <{id}> resulted in stack overflow.")
|
||||||
|
}
|
||||||
|
ErrorKind::ScopeExit => "Exited the last scope. This is a logic bug.".fmt(f),
|
||||||
|
ErrorKind::TypeError => "Incompatible types".fmt(f),
|
||||||
|
ErrorKind::NotIterable => "`in` clause of `for` loop did not yield an iterable".fmt(f),
|
||||||
|
ErrorKind::NotIndexable => {
|
||||||
|
write!(f, "expression cannot be indexed")
|
||||||
|
}
|
||||||
|
ErrorKind::OobIndex(idx, len) => {
|
||||||
|
write!(f, "Index out of bounds: index was {idx}. but len is {len}")
|
||||||
|
}
|
||||||
|
ErrorKind::NotAssignable => {
|
||||||
|
write!(f, "expression is not assignable")
|
||||||
|
}
|
||||||
|
ErrorKind::NotDefined(value) => {
|
||||||
|
write!(f, "{value} not bound. Did you mean `let {value};`?")
|
||||||
|
}
|
||||||
|
ErrorKind::NotInitialized(value) => {
|
||||||
|
write!(f, "{value} bound, but not initialized")
|
||||||
|
}
|
||||||
|
ErrorKind::NotCallable(value) => {
|
||||||
|
write!(f, "{value} is not callable.")
|
||||||
|
}
|
||||||
|
ErrorKind::ArgNumber { want, got } => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Expected {want} argument{}, got {got}",
|
||||||
|
if *want == 1 { "" } else { "s" }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ErrorKind::PatFailed(pattern) => {
|
||||||
|
write!(f, "Failed to match pattern {pattern}")
|
||||||
|
}
|
||||||
|
ErrorKind::MatchNonexhaustive => {
|
||||||
|
write!(f, "Fell through a non-exhaustive match expression!")
|
||||||
|
}
|
||||||
|
ErrorKind::Panic(s, _depth) => write!(f, "Explicit panic: {s}"),
|
||||||
|
ErrorKind::BuiltinError(s) => write!(f, "{s}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
83
compiler/cl-interpret/src/function.rs
Normal file
83
compiler/cl-interpret/src/function.rs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
//! Represents a block of code which lives inside the Interpreter
|
||||||
|
|
||||||
|
use collect_upvars::collect_upvars;
|
||||||
|
|
||||||
|
use crate::error::ErrorKind;
|
||||||
|
|
||||||
|
use super::{Callable, ConValue, Environment, Error, IResult, Interpret, pattern};
|
||||||
|
use cl_ast::{Function as FnDecl, Sym};
|
||||||
|
use std::{
|
||||||
|
cell::{Ref, RefCell},
|
||||||
|
collections::HashMap,
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mod collect_upvars;
|
||||||
|
|
||||||
|
type Upvars = HashMap<Sym, ConValue>;
|
||||||
|
|
||||||
|
/// Represents a block of code which persists inside the Interpreter
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Function {
|
||||||
|
/// Stores the contents of the function declaration
|
||||||
|
decl: Rc<FnDecl>,
|
||||||
|
/// Stores data from the enclosing scopes
|
||||||
|
upvars: RefCell<Upvars>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Function {
|
||||||
|
pub fn new(decl: &FnDecl) -> Self {
|
||||||
|
// let upvars = collect_upvars(decl, env);
|
||||||
|
Self { decl: decl.clone().into(), upvars: Default::default() }
|
||||||
|
}
|
||||||
|
pub fn decl(&self) -> &FnDecl {
|
||||||
|
&self.decl
|
||||||
|
}
|
||||||
|
pub fn upvars(&self) -> Ref<'_, Upvars> {
|
||||||
|
self.upvars.borrow()
|
||||||
|
}
|
||||||
|
pub fn lift_upvars(&self, env: &Environment) {
|
||||||
|
let upvars = collect_upvars(&self.decl, env);
|
||||||
|
if let Ok(mut self_upvars) = self.upvars.try_borrow_mut() {
|
||||||
|
*self_upvars = upvars;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Callable for Function {
|
||||||
|
fn name(&self) -> Sym {
|
||||||
|
let FnDecl { name, .. } = *self.decl;
|
||||||
|
name
|
||||||
|
}
|
||||||
|
fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
|
||||||
|
let FnDecl { name, gens: _, bind, body, sign: _ } = &*self.decl;
|
||||||
|
|
||||||
|
// Check arg mapping
|
||||||
|
let Some(body) = body else {
|
||||||
|
return Err(Error::NotDefined(*name));
|
||||||
|
};
|
||||||
|
|
||||||
|
let upvars = self.upvars.take();
|
||||||
|
let mut env = env.with_frame("upvars", upvars);
|
||||||
|
|
||||||
|
// TODO: completely refactor data storage
|
||||||
|
let mut frame = env.frame("fn args");
|
||||||
|
for (name, value) in pattern::substitution(&frame, bind, ConValue::Tuple(args.into()))? {
|
||||||
|
frame.insert(name, value);
|
||||||
|
}
|
||||||
|
let res = body.interpret(&mut frame);
|
||||||
|
drop(frame);
|
||||||
|
if let Some(upvars) = env.pop_values() {
|
||||||
|
self.upvars.replace(upvars);
|
||||||
|
}
|
||||||
|
match res {
|
||||||
|
Err(Error { kind: ErrorKind::Return(value), .. }) => Ok(value),
|
||||||
|
Err(Error { kind: ErrorKind::Break(value), .. }) => Err(Error::BadBreak(value)),
|
||||||
|
Err(Error { kind: ErrorKind::Panic(msg, depth), span: Some(span) }) => {
|
||||||
|
println!("{depth:>4}: {name}{bind} at {}", span.head);
|
||||||
|
Err(Error { kind: ErrorKind::Panic(msg, depth + 1), span: None })
|
||||||
|
}
|
||||||
|
other => other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
112
compiler/cl-interpret/src/function/collect_upvars.rs
Normal file
112
compiler/cl-interpret/src/function/collect_upvars.rs
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
//! Collects the "Upvars" of a function at the point of its creation, allowing variable capture
|
||||||
|
use crate::env::Environment;
|
||||||
|
use cl_ast::{
|
||||||
|
Function, Let, Path, PathPart, Pattern, Sym,
|
||||||
|
ast_visitor::{visit::*, walk::Walk},
|
||||||
|
};
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
|
pub fn collect_upvars(f: &Function, env: &Environment) -> super::Upvars {
|
||||||
|
CollectUpvars::new(env).visit(f).finish_copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct CollectUpvars<'env> {
|
||||||
|
env: &'env Environment,
|
||||||
|
upvars: HashMap<Sym, usize>,
|
||||||
|
blacklist: HashSet<Sym>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'env> CollectUpvars<'env> {
|
||||||
|
pub fn new(env: &'env Environment) -> Self {
|
||||||
|
Self { upvars: HashMap::new(), blacklist: HashSet::new(), env }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish(&mut self) -> HashMap<Sym, usize> {
|
||||||
|
std::mem::take(&mut self.upvars)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish_copied(&mut self) -> super::Upvars {
|
||||||
|
let Self { env, upvars, blacklist: _ } = self;
|
||||||
|
std::mem::take(upvars)
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|(k, v)| env.get_id(v).cloned().map(|v| (k, v)))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_upvar(&mut self, name: &Sym) {
|
||||||
|
let Self { env, upvars, blacklist } = self;
|
||||||
|
if blacklist.contains(name) || upvars.contains_key(name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Ok(place) = env.id_of(*name) {
|
||||||
|
upvars.insert(*name, place);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bind_name(&mut self, name: &Sym) {
|
||||||
|
self.blacklist.insert(*name);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scope(&mut self, f: impl Fn(&mut CollectUpvars<'env>)) {
|
||||||
|
let blacklist = self.blacklist.clone();
|
||||||
|
|
||||||
|
// visit the scope
|
||||||
|
f(self);
|
||||||
|
|
||||||
|
// restore the blacklist
|
||||||
|
self.blacklist = blacklist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Visit<'a> for CollectUpvars<'_> {
|
||||||
|
fn visit_block(&mut self, b: &'a cl_ast::Block) {
|
||||||
|
self.scope(|cu| b.children(cu));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_let(&mut self, l: &'a cl_ast::Let) {
|
||||||
|
let Let { mutable, name, ty, init } = l;
|
||||||
|
self.visit_mutability(mutable);
|
||||||
|
|
||||||
|
ty.visit_in(self);
|
||||||
|
// visit the initializer, which may use the bound name
|
||||||
|
init.visit_in(self);
|
||||||
|
// a bound name can never be an upvar
|
||||||
|
self.visit_pattern(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_path(&mut self, p: &'a cl_ast::Path) {
|
||||||
|
// TODO: path resolution in environments
|
||||||
|
let Path { absolute: false, parts } = p else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let [PathPart::Ident(name)] = parts.as_slice() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
self.add_upvar(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_fielder(&mut self, f: &'a cl_ast::Fielder) {
|
||||||
|
let cl_ast::Fielder { name, init } = f;
|
||||||
|
if let Some(init) = init {
|
||||||
|
self.visit_expr(init);
|
||||||
|
} else {
|
||||||
|
self.add_upvar(name); // fielder without init grabs from env
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_pattern(&mut self, value: &'a cl_ast::Pattern) {
|
||||||
|
match value {
|
||||||
|
Pattern::Name(name) => {
|
||||||
|
self.bind_name(name);
|
||||||
|
}
|
||||||
|
Pattern::RangeExc(_, _) | Pattern::RangeInc(_, _) => {}
|
||||||
|
_ => value.children(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_match_arm(&mut self, value: &'a cl_ast::MatchArm) {
|
||||||
|
// MatchArms bind variables with a very small local scope
|
||||||
|
self.scope(|cu| value.children(cu));
|
||||||
|
}
|
||||||
|
}
|
||||||
1064
compiler/cl-interpret/src/interpret.rs
Normal file
1064
compiler/cl-interpret/src/interpret.rs
Normal file
File diff suppressed because it is too large
Load Diff
671
compiler/cl-interpret/src/lib.rs
Normal file
671
compiler/cl-interpret/src/lib.rs
Normal file
@@ -0,0 +1,671 @@
|
|||||||
|
//! Walks a Conlang AST, interpreting it as a program.
|
||||||
|
#![warn(clippy::all)]
|
||||||
|
#![feature(decl_macro)]
|
||||||
|
|
||||||
|
use cl_ast::Sym;
|
||||||
|
use convalue::ConValue;
|
||||||
|
use env::Environment;
|
||||||
|
use error::{Error, ErrorKind, IResult};
|
||||||
|
use interpret::Interpret;
|
||||||
|
|
||||||
|
/// Callable types can be called from within a Conlang program
|
||||||
|
pub trait Callable {
|
||||||
|
/// Calls this [Callable] in the provided [Environment], with [ConValue] args \
|
||||||
|
/// The Callable is responsible for checking the argument count and validating types
|
||||||
|
fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue>;
|
||||||
|
/// Returns the common name of this identifier.
|
||||||
|
fn name(&self) -> Sym;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod convalue;
|
||||||
|
|
||||||
|
pub mod interpret;
|
||||||
|
|
||||||
|
pub mod function;
|
||||||
|
|
||||||
|
pub mod constructor {
|
||||||
|
use cl_ast::Sym;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
Callable,
|
||||||
|
convalue::ConValue,
|
||||||
|
env::Environment,
|
||||||
|
error::{Error, IResult},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Constructor {
|
||||||
|
pub name: Sym,
|
||||||
|
pub arity: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Callable for Constructor {
|
||||||
|
fn call(&self, _env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
|
||||||
|
let &Self { name, arity } = self;
|
||||||
|
if arity as usize == args.len() {
|
||||||
|
Ok(ConValue::TupleStruct(name, Box::new(args.into())))
|
||||||
|
} else {
|
||||||
|
Err(Error::ArgNumber(arity as usize, args.len()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> cl_ast::Sym {
|
||||||
|
"tuple-constructor".into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod closure;
|
||||||
|
|
||||||
|
pub mod builtin;
|
||||||
|
|
||||||
|
pub mod pattern;
|
||||||
|
|
||||||
|
pub mod env;
|
||||||
|
|
||||||
|
pub mod modules {
|
||||||
|
use crate::env::StackBinds;
|
||||||
|
use cl_ast::{PathPart, Sym};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
/// Immutable object-oriented interface to a [ModuleTree]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct ModuleNode<'tree> {
|
||||||
|
tree: &'tree ModuleTree,
|
||||||
|
index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mutable object-oriented interface to a [ModuleTree]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ModuleNodeMut<'tree> {
|
||||||
|
tree: &'tree mut ModuleTree,
|
||||||
|
index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! module_node_impl {
|
||||||
|
() => {
|
||||||
|
/// Gets the index from this node
|
||||||
|
pub fn index(self) -> usize {
|
||||||
|
self.index
|
||||||
|
}
|
||||||
|
/// Gets this node's parent
|
||||||
|
pub fn parent(self) -> Option<Self> {
|
||||||
|
let parent = self.tree.parent(self.index)?;
|
||||||
|
Some(Self { index: parent, ..self })
|
||||||
|
}
|
||||||
|
/// Gets the node's "encompassing Type"
|
||||||
|
pub fn selfty(self) -> Option<Self> {
|
||||||
|
let selfty = self.tree.selfty(self.index)?;
|
||||||
|
Some(Self { index: selfty, ..self })
|
||||||
|
}
|
||||||
|
/// Gets the child of this node with the given name
|
||||||
|
pub fn child(self, name: &Sym) -> Option<Self> {
|
||||||
|
let child = self.tree.child(self.index, name)?;
|
||||||
|
Some(Self { index: child, ..self })
|
||||||
|
}
|
||||||
|
/// Gets a stack value in this node with the given name
|
||||||
|
pub fn item(self, name: &Sym) -> Option<usize> {
|
||||||
|
self.tree.items(self.index)?.get(name).copied()
|
||||||
|
}
|
||||||
|
/// Returns true when this node represents type information
|
||||||
|
pub fn is_ty(self) -> Option<bool> {
|
||||||
|
self.tree.is_ty.get(self.index).copied()
|
||||||
|
}
|
||||||
|
/// Returns a reference to this node's children, if present
|
||||||
|
pub fn children(&self) -> Option<&HashMap<Sym, usize>> {
|
||||||
|
self.tree.children(self.index)
|
||||||
|
}
|
||||||
|
/// Returns a reference to this node's items, if present
|
||||||
|
pub fn items(&self) -> Option<&StackBinds> {
|
||||||
|
self.tree.items(self.index)
|
||||||
|
}
|
||||||
|
/// Traverses a path starting at this node
|
||||||
|
///
|
||||||
|
/// Returns a new node, and the unconsumed path portion.
|
||||||
|
pub fn find(self, path: &[PathPart]) -> (Self, &[PathPart]) {
|
||||||
|
let (index, path) = self.tree.find(self.index, path);
|
||||||
|
(Self { index, ..self }, path)
|
||||||
|
}
|
||||||
|
/// Traverses a path starting at this node
|
||||||
|
///
|
||||||
|
/// Returns an item address if the path terminated in an item.
|
||||||
|
pub fn find_item(&self, path: &[PathPart]) -> Option<usize> {
|
||||||
|
self.tree.find_item(self.index, path)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleNode<'_> {
|
||||||
|
module_node_impl! {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleNodeMut<'_> {
|
||||||
|
module_node_impl! {}
|
||||||
|
/// Creates a new child in this node
|
||||||
|
pub fn add_child(self, name: Sym, is_ty: bool) -> Self {
|
||||||
|
let node = self.tree.add_child(self.index, name, is_ty);
|
||||||
|
self.tree.get_mut(node)
|
||||||
|
}
|
||||||
|
/// Creates an arbitrary edge in the module graph
|
||||||
|
pub fn add_import(&mut self, name: Sym, child: usize) {
|
||||||
|
self.tree.add_import(self.index, name, child)
|
||||||
|
}
|
||||||
|
pub fn add_imports(&mut self, binds: HashMap<Sym, usize>) {
|
||||||
|
self.tree.add_imports(self.index, binds)
|
||||||
|
}
|
||||||
|
/// Binds a new item in this node
|
||||||
|
pub fn add_item(&mut self, name: Sym, stack_index: usize) {
|
||||||
|
self.tree.add_item(self.index, name, stack_index)
|
||||||
|
}
|
||||||
|
/// Binds an entire stack frame in this node
|
||||||
|
pub fn add_items(&mut self, binds: StackBinds) {
|
||||||
|
self.tree.add_items(self.index, binds)
|
||||||
|
}
|
||||||
|
/// Constructs a borrowing [ModuleNode]
|
||||||
|
pub fn as_ref(&self) -> ModuleNode<'_> {
|
||||||
|
let Self { tree, index } = self;
|
||||||
|
ModuleNode { tree, index: *index }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ModuleTree {
|
||||||
|
parents: Vec<usize>,
|
||||||
|
children: Vec<HashMap<Sym, usize>>,
|
||||||
|
items: Vec<StackBinds>,
|
||||||
|
is_ty: Vec<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleTree {
|
||||||
|
/// Constructs a new ModuleTree with a single root module
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
parents: vec![0],
|
||||||
|
children: vec![HashMap::new()],
|
||||||
|
items: vec![HashMap::new()],
|
||||||
|
is_ty: vec![false],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a borrowed handle to the node at `index`
|
||||||
|
pub fn get(&self, index: usize) -> ModuleNode<'_> {
|
||||||
|
ModuleNode { tree: self, index }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a mutable handle to the node at `index`
|
||||||
|
pub fn get_mut(&mut self, index: usize) -> ModuleNodeMut<'_> {
|
||||||
|
ModuleNodeMut { tree: self, index }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new child in this node
|
||||||
|
pub fn add_child(&mut self, parent: usize, name: Sym, is_ty: bool) -> usize {
|
||||||
|
let index = self.parents.len();
|
||||||
|
self.children[parent].insert(name, index);
|
||||||
|
self.parents.push(parent);
|
||||||
|
self.children.push(HashMap::new());
|
||||||
|
self.is_ty.push(is_ty);
|
||||||
|
index
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Binds a new item in this node
|
||||||
|
pub fn add_item(&mut self, node: usize, name: Sym, stack_index: usize) {
|
||||||
|
self.items[node].insert(name, stack_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an arbitrary child edge
|
||||||
|
pub fn add_import(&mut self, parent: usize, name: Sym, child: usize) {
|
||||||
|
self.children[parent].insert(name, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Binds an entire stack frame in this node
|
||||||
|
pub fn add_items(&mut self, node: usize, binds: StackBinds) {
|
||||||
|
self.items[node].extend(binds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Binds an arbitrary set of child edges
|
||||||
|
pub fn add_imports(&mut self, node: usize, binds: HashMap<Sym, usize>) {
|
||||||
|
self.children[node].extend(binds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets this node's parent
|
||||||
|
pub fn parent(&self, node: usize) -> Option<usize> {
|
||||||
|
if node == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
self.parents.get(node).copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the node's "encompassing Type"
|
||||||
|
pub fn selfty(&self, node: usize) -> Option<usize> {
|
||||||
|
if self.is_ty[node] {
|
||||||
|
return Some(node);
|
||||||
|
}
|
||||||
|
self.selfty(self.parent(node)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the child of this node with the given name
|
||||||
|
pub fn child(&self, node: usize, id: &Sym) -> Option<usize> {
|
||||||
|
self.children[node].get(id).copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a stack value in this node with the given name
|
||||||
|
pub fn item(&self, node: usize, name: &Sym) -> Option<usize> {
|
||||||
|
self.items.get(node).and_then(|map| map.get(name).copied())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to this node's children, if present
|
||||||
|
pub fn children(&self, node: usize) -> Option<&HashMap<Sym, usize>> {
|
||||||
|
self.children.get(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to this node's items, if present
|
||||||
|
pub fn items(&self, node: usize) -> Option<&StackBinds> {
|
||||||
|
self.items.get(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Traverses a path starting at this node
|
||||||
|
///
|
||||||
|
/// Returns a new node, and the unconsumed path portion.
|
||||||
|
pub fn find<'p>(&self, node: usize, path: &'p [PathPart]) -> (usize, &'p [PathPart]) {
|
||||||
|
match path {
|
||||||
|
[PathPart::SuperKw, tail @ ..] => match self.parent(node) {
|
||||||
|
Some(node) => self.find(node, tail),
|
||||||
|
None => (node, path),
|
||||||
|
},
|
||||||
|
[PathPart::Ident(name), tail @ ..] => match self.child(node, name) {
|
||||||
|
Some(node) => self.find(node, tail),
|
||||||
|
None => (node, path),
|
||||||
|
},
|
||||||
|
[PathPart::SelfTy, tail @ ..] => match self.selfty(node) {
|
||||||
|
Some(node) => self.find(node, tail),
|
||||||
|
None => (node, path),
|
||||||
|
},
|
||||||
|
[] => (node, path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Traverses a path starting at this node
|
||||||
|
///
|
||||||
|
/// Returns an item address if the path terminated in an item.
|
||||||
|
pub fn find_item(&self, node: usize, path: &[PathPart]) -> Option<usize> {
|
||||||
|
let (node, [PathPart::Ident(name)]) = self.find(node, path) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
self.item(node, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ModuleTree {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod collector {
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
convalue::ConValue,
|
||||||
|
env::Environment,
|
||||||
|
modules::{ModuleNode, ModuleNodeMut},
|
||||||
|
};
|
||||||
|
use cl_ast::{
|
||||||
|
ast_visitor::{Visit, Walk},
|
||||||
|
*,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Collector<'env> {
|
||||||
|
module: usize,
|
||||||
|
env: &'env mut Environment,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Collector<'_> {
|
||||||
|
pub fn as_node(&self) -> ModuleNode<'_> {
|
||||||
|
self.env.modules().get(self.module)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_node_mut(&mut self) -> ModuleNodeMut<'_> {
|
||||||
|
self.env.modules_mut().get_mut(self.module)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scope(&mut self, name: Sym, is_ty: bool, f: impl Fn(&mut Collector<'_>)) {
|
||||||
|
let module = match self.as_node_mut().child(&name) {
|
||||||
|
Some(m) => m,
|
||||||
|
None => self.as_node_mut().add_child(name, is_ty),
|
||||||
|
}
|
||||||
|
.index();
|
||||||
|
|
||||||
|
let mut frame = self.env.frame(name.to_ref());
|
||||||
|
f(&mut Collector { env: &mut frame, module });
|
||||||
|
let binds = frame.into_binds().unwrap_or_default();
|
||||||
|
|
||||||
|
self.modules_mut().add_items(module, binds);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn in_foreign_scope<F, T>(&mut self, path: &[PathPart], f: F) -> Option<T>
|
||||||
|
where F: Fn(&mut Collector<'_>) -> T {
|
||||||
|
let (module, []) = self.env.modules_mut().find(self.module, path) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut frame = self.env.frame("impl");
|
||||||
|
let out = f(&mut Collector { env: &mut frame, module });
|
||||||
|
let binds = frame.into_binds().unwrap_or_default();
|
||||||
|
|
||||||
|
self.env.modules_mut().add_items(module, binds);
|
||||||
|
Some(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'env> Deref for Collector<'env> {
|
||||||
|
type Target = Environment;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.env
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Collector<'_> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
self.env
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'env> Visit<'a> for Collector<'env> {
|
||||||
|
fn visit_file(&mut self, value: &'a File) {
|
||||||
|
let mut sorter = ItemSorter::default();
|
||||||
|
sorter.visit(value);
|
||||||
|
sorter.visit_all(self);
|
||||||
|
}
|
||||||
|
fn visit_block(&mut self, value: &'a Block) {
|
||||||
|
let mut sorter = ItemSorter::default();
|
||||||
|
sorter.visit(value);
|
||||||
|
sorter.visit_all(self);
|
||||||
|
}
|
||||||
|
fn visit_module(&mut self, value: &'a cl_ast::Module) {
|
||||||
|
self.scope(value.name, false, |scope| value.children(scope));
|
||||||
|
}
|
||||||
|
fn visit_alias(&mut self, value: &'a cl_ast::Alias) {
|
||||||
|
let Alias { name, from } = value;
|
||||||
|
match from.as_ref().map(Box::as_ref) {
|
||||||
|
Some(Ty { kind: TyKind::Path(path), .. }) => {
|
||||||
|
let mut node = if path.absolute {
|
||||||
|
self.modules_mut().get_mut(0)
|
||||||
|
} else {
|
||||||
|
self.as_node_mut()
|
||||||
|
};
|
||||||
|
if let Some(item) = node.find_item(&path.parts) {
|
||||||
|
node.add_item(*name, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(other) => todo!("Type expressions in the collector: {other}"),
|
||||||
|
None => self.scope(*name, true, |_| {}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn visit_enum(&mut self, value: &'a cl_ast::Enum) {
|
||||||
|
let Enum { name, gens: _, variants } = value;
|
||||||
|
|
||||||
|
self.scope(*name, true, |frame| {
|
||||||
|
for (idx, Variant { name, kind, body }) in variants.iter().enumerate() {
|
||||||
|
frame.visit(body);
|
||||||
|
frame.scope(*name, false, |frame| {
|
||||||
|
frame.bind("__discriminant", idx as isize);
|
||||||
|
match kind {
|
||||||
|
StructKind::Empty => {
|
||||||
|
frame.insert_tup_constructor("call".into(), 0);
|
||||||
|
frame.bind("__nmemb", ConValue::Int(0));
|
||||||
|
}
|
||||||
|
StructKind::Tuple(args) => {
|
||||||
|
// Constructs the AST from scratch. TODO: This, better.
|
||||||
|
frame.insert_tup_constructor("call".into(), args.len());
|
||||||
|
frame.bind("__nmemb", ConValue::Int(args.len() as _));
|
||||||
|
}
|
||||||
|
StructKind::Struct(members) => {
|
||||||
|
// TODO: more precise type checking of structs
|
||||||
|
for (idx, memb) in members.iter().enumerate() {
|
||||||
|
let StructMember { vis: _, name, ty: _ } = memb;
|
||||||
|
frame.bind(*name, idx as isize);
|
||||||
|
}
|
||||||
|
frame.bind("__nmemb", ConValue::Int(members.len() as _));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fn visit_struct(&mut self, value: &'a cl_ast::Struct) {
|
||||||
|
let Struct { name, gens: _, kind } = value;
|
||||||
|
|
||||||
|
self.scope(*name, true, |frame| {
|
||||||
|
match kind {
|
||||||
|
StructKind::Empty => {
|
||||||
|
frame.insert_tup_constructor("call".into(), 0);
|
||||||
|
frame.bind("__nmemb", ConValue::Int(0));
|
||||||
|
}
|
||||||
|
StructKind::Tuple(args) => {
|
||||||
|
// Constructs the AST from scratch. TODO: This, better.
|
||||||
|
frame.insert_tup_constructor("call".into(), args.len());
|
||||||
|
frame.bind("__nmemb", ConValue::Int(args.len() as _));
|
||||||
|
}
|
||||||
|
StructKind::Struct(members) => {
|
||||||
|
// TODO: more precise type checking of structs
|
||||||
|
for (idx, memb) in members.iter().enumerate() {
|
||||||
|
let StructMember { vis: _, name, ty: _ } = memb;
|
||||||
|
frame.bind(*name, idx as isize);
|
||||||
|
}
|
||||||
|
frame.bind("__nmemb", ConValue::Int(members.len() as _));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fn visit_const(&mut self, value: &'a cl_ast::Const) {
|
||||||
|
let Const { name, ty: _, init } = value;
|
||||||
|
self.visit(init);
|
||||||
|
self.bind(*name, ());
|
||||||
|
}
|
||||||
|
fn visit_static(&mut self, value: &'a cl_ast::Static) {
|
||||||
|
let Static { mutable: _, name, ty: _, init } = value;
|
||||||
|
self.visit(init);
|
||||||
|
self.bind(*name, ());
|
||||||
|
}
|
||||||
|
fn visit_function(&mut self, value: &'a cl_ast::Function) {
|
||||||
|
let Function { name, gens: _, sign: _, bind: _, body } = value;
|
||||||
|
self.scope(*name, false, |scope| {
|
||||||
|
scope.visit(body);
|
||||||
|
let f = crate::function::Function::new(value);
|
||||||
|
scope.bind("call", f);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fn visit_impl(&mut self, value: &'a cl_ast::Impl) {
|
||||||
|
let Impl { gens: _, target: ImplKind::Type(Ty { kind: TyKind::Path(name), .. }), body } =
|
||||||
|
value
|
||||||
|
else {
|
||||||
|
eprintln!("TODO: impl X for Ty");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
self.in_foreign_scope(&name.parts, |scope| {
|
||||||
|
body.visit_in(scope);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fn visit_use(&mut self, value: &'a cl_ast::Use) {
|
||||||
|
fn traverse(dest: &mut Collector<'_>, node: usize, tree: &UseTree) {
|
||||||
|
match tree {
|
||||||
|
UseTree::Tree(ts) => ts.iter().for_each(|tree| traverse(dest, node, tree)),
|
||||||
|
UseTree::Path(PathPart::Ident(name), tree) => {
|
||||||
|
if let (node, []) = dest.modules().find(node, &[PathPart::Ident(*name)]) {
|
||||||
|
traverse(dest, node, tree)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UseTree::Path(PathPart::SuperKw, tree) => {
|
||||||
|
if let Some(node) = dest.modules().parent(node) {
|
||||||
|
traverse(dest, node, tree)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UseTree::Path(PathPart::SelfTy, tree) => {
|
||||||
|
if let Some(node) = dest.modules().selfty(node) {
|
||||||
|
traverse(dest, node, tree)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UseTree::Alias(name, as_name) => {
|
||||||
|
if let Some(child) = dest.modules().child(node, name) {
|
||||||
|
dest.as_node_mut().add_import(*as_name, child);
|
||||||
|
}
|
||||||
|
if let Some(item) = dest.modules().item(node, name) {
|
||||||
|
dest.as_node_mut().add_item(*as_name, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UseTree::Name(name) => {
|
||||||
|
if let Some(child) = dest.modules().child(node, name) {
|
||||||
|
dest.as_node_mut().add_import(*name, child);
|
||||||
|
}
|
||||||
|
if let Some(item) = dest.modules().item(node, name) {
|
||||||
|
dest.as_node_mut().add_item(*name, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UseTree::Glob => {
|
||||||
|
let &mut Collector { module, ref mut env } = dest;
|
||||||
|
if let Some(children) = env.modules().children(node) {
|
||||||
|
for (name, index) in children.clone() {
|
||||||
|
env.modules_mut().add_import(module, name, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(items) = env.modules().items(node).cloned() {
|
||||||
|
env.modules_mut().add_items(node, items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let Use { absolute, tree } = value;
|
||||||
|
let node = if *absolute { 0 } else { self.module };
|
||||||
|
traverse(self, node, tree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn make_tuple_constructor(name: Sym, args: &[Ty]) -> ConValue {
|
||||||
|
// let span = match (
|
||||||
|
// args.first().map(|a| a.span.head),
|
||||||
|
// args.last().map(|a| a.span.tail),
|
||||||
|
// ) {
|
||||||
|
// (Some(head), Some(tail)) => Span(head, tail),
|
||||||
|
// _ => Span::dummy(),
|
||||||
|
// };
|
||||||
|
|
||||||
|
// let constructor = Function {
|
||||||
|
// name,
|
||||||
|
// gens: Default::default(),
|
||||||
|
// sign: TyFn {
|
||||||
|
// args: Ty { kind: TyKind::Tuple(TyTuple { types: args.to_vec() }), span }.into(),
|
||||||
|
// rety: Some(Ty { span: Span::dummy(), kind: TyKind::Path(Path::from(name))
|
||||||
|
// }.into()), },
|
||||||
|
// bind: Pattern::Tuple(
|
||||||
|
// args.iter()
|
||||||
|
// .enumerate()
|
||||||
|
// .map(|(idx, _)| Pattern::Name(idx.to_string().into()))
|
||||||
|
// .collect(),
|
||||||
|
// ),
|
||||||
|
// body: None,
|
||||||
|
// };
|
||||||
|
// // ConValue::TupleConstructor(crate::constructor::Constructor {ind})
|
||||||
|
// todo!("Tuple constructor {constructor}")
|
||||||
|
// }
|
||||||
|
|
||||||
|
/// Sorts items
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct ItemSorter<'ast> {
|
||||||
|
modules: Vec<&'ast Module>,
|
||||||
|
structs: Vec<&'ast Struct>,
|
||||||
|
enums: Vec<&'ast Enum>,
|
||||||
|
aliases: Vec<&'ast Alias>,
|
||||||
|
consts: Vec<&'ast Const>,
|
||||||
|
statics: Vec<&'ast Static>,
|
||||||
|
functions: Vec<&'ast Function>,
|
||||||
|
impls: Vec<&'ast Impl>,
|
||||||
|
imports: Vec<&'ast Use>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ItemSorter<'a> {
|
||||||
|
fn visit_all<V: Visit<'a>>(&self, v: &mut V) {
|
||||||
|
let Self {
|
||||||
|
modules,
|
||||||
|
aliases,
|
||||||
|
enums,
|
||||||
|
structs,
|
||||||
|
consts,
|
||||||
|
statics,
|
||||||
|
functions,
|
||||||
|
impls,
|
||||||
|
imports,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
// 0
|
||||||
|
for item in modules {
|
||||||
|
item.visit_in(v);
|
||||||
|
}
|
||||||
|
// 1
|
||||||
|
for item in structs {
|
||||||
|
item.visit_in(v);
|
||||||
|
}
|
||||||
|
for item in enums {
|
||||||
|
item.visit_in(v);
|
||||||
|
}
|
||||||
|
for item in aliases {
|
||||||
|
item.visit_in(v);
|
||||||
|
}
|
||||||
|
// 2
|
||||||
|
// 5
|
||||||
|
for item in consts {
|
||||||
|
item.visit_in(v);
|
||||||
|
}
|
||||||
|
for item in statics {
|
||||||
|
item.visit_in(v);
|
||||||
|
}
|
||||||
|
for item in functions {
|
||||||
|
item.visit_in(v);
|
||||||
|
}
|
||||||
|
// 4
|
||||||
|
for item in impls {
|
||||||
|
item.visit_in(v);
|
||||||
|
}
|
||||||
|
// 3
|
||||||
|
for item in imports {
|
||||||
|
item.visit_in(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Visit<'a> for ItemSorter<'a> {
|
||||||
|
fn visit_module(&mut self, value: &'a cl_ast::Module) {
|
||||||
|
self.modules.push(value);
|
||||||
|
}
|
||||||
|
fn visit_alias(&mut self, value: &'a cl_ast::Alias) {
|
||||||
|
self.aliases.push(value);
|
||||||
|
}
|
||||||
|
fn visit_enum(&mut self, value: &'a cl_ast::Enum) {
|
||||||
|
self.enums.push(value);
|
||||||
|
}
|
||||||
|
fn visit_struct(&mut self, value: &'a cl_ast::Struct) {
|
||||||
|
self.structs.push(value);
|
||||||
|
}
|
||||||
|
fn visit_const(&mut self, value: &'a cl_ast::Const) {
|
||||||
|
self.consts.push(value);
|
||||||
|
}
|
||||||
|
fn visit_static(&mut self, value: &'a cl_ast::Static) {
|
||||||
|
self.statics.push(value);
|
||||||
|
}
|
||||||
|
fn visit_function(&mut self, value: &'a cl_ast::Function) {
|
||||||
|
self.functions.push(value);
|
||||||
|
}
|
||||||
|
fn visit_impl(&mut self, value: &'a cl_ast::Impl) {
|
||||||
|
self.impls.push(value);
|
||||||
|
}
|
||||||
|
fn visit_use(&mut self, value: &'a cl_ast::Use) {
|
||||||
|
self.imports.push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
347
compiler/cl-interpret/src/pattern.rs
Normal file
347
compiler/cl-interpret/src/pattern.rs
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
//! Unification algorithm for cl-ast [Pattern]s and [ConValue]s
|
||||||
|
//!
|
||||||
|
//! [`variables()`] returns a flat list of symbols that are bound by a given pattern
|
||||||
|
//! [`substitution()`] unifies a ConValue with a pattern, and produces a list of bound names
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
convalue::ConValue,
|
||||||
|
env::Environment,
|
||||||
|
error::{Error, IResult},
|
||||||
|
};
|
||||||
|
use cl_ast::{Literal, Pattern, Sym};
|
||||||
|
use std::collections::{HashMap, VecDeque};
|
||||||
|
|
||||||
|
/// Gets the path variables in the given Pattern
|
||||||
|
pub fn variables(pat: &Pattern) -> Vec<&Sym> {
|
||||||
|
fn patvars<'p>(set: &mut Vec<&'p Sym>, pat: &'p Pattern) {
|
||||||
|
match pat {
|
||||||
|
Pattern::Name(name) if name.to_ref() == "_" => {}
|
||||||
|
Pattern::Name(name) => set.push(name),
|
||||||
|
Pattern::Path(_) => {}
|
||||||
|
Pattern::Literal(_) => {}
|
||||||
|
Pattern::Rest(Some(pattern)) => patvars(set, pattern),
|
||||||
|
Pattern::Rest(None) => {}
|
||||||
|
Pattern::Ref(_, pattern) => patvars(set, pattern),
|
||||||
|
Pattern::RangeExc(_, _) => {}
|
||||||
|
Pattern::RangeInc(_, _) => {}
|
||||||
|
Pattern::Tuple(patterns) | Pattern::Array(patterns) => {
|
||||||
|
patterns.iter().for_each(|pat| patvars(set, pat))
|
||||||
|
}
|
||||||
|
Pattern::Struct(_path, items) => {
|
||||||
|
items.iter().for_each(|(name, pat)| match pat {
|
||||||
|
Some(pat) => patvars(set, pat),
|
||||||
|
None => set.push(name),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Pattern::TupleStruct(_path, items) => {
|
||||||
|
items.iter().for_each(|pat| patvars(set, pat));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut set = Vec::new();
|
||||||
|
patvars(&mut set, pat);
|
||||||
|
set
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rest_binding<'pat>(
|
||||||
|
env: &Environment,
|
||||||
|
sub: &mut HashMap<Sym, ConValue>,
|
||||||
|
mut patterns: &'pat [Pattern],
|
||||||
|
mut values: VecDeque<ConValue>,
|
||||||
|
) -> IResult<Option<(&'pat Pattern, VecDeque<ConValue>)>> {
|
||||||
|
// Bind the head of the list
|
||||||
|
while let [pattern, tail @ ..] = patterns {
|
||||||
|
if matches!(pattern, Pattern::Rest(_)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let value = values
|
||||||
|
.pop_front()
|
||||||
|
.ok_or_else(|| Error::PatFailed(Box::new(pattern.clone())))?;
|
||||||
|
append_sub(env, sub, pattern, value)?;
|
||||||
|
patterns = tail;
|
||||||
|
}
|
||||||
|
// Bind the tail of the list
|
||||||
|
while let [head @ .., pattern] = patterns {
|
||||||
|
if matches!(pattern, Pattern::Rest(_)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let value = values
|
||||||
|
.pop_back()
|
||||||
|
.ok_or_else(|| Error::PatFailed(Box::new(pattern.clone())))?;
|
||||||
|
append_sub(env, sub, pattern, value)?;
|
||||||
|
patterns = head;
|
||||||
|
}
|
||||||
|
// Bind the ..rest of the list
|
||||||
|
match patterns {
|
||||||
|
[] | [Pattern::Rest(None)] => Ok(None),
|
||||||
|
[Pattern::Rest(Some(pattern))] => Ok(Some((pattern.as_ref(), values))),
|
||||||
|
_ => Err(Error::PatFailed(Box::new(Pattern::Array(patterns.into())))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rest_binding_ref<'pat>(
|
||||||
|
env: &Environment,
|
||||||
|
sub: &mut HashMap<Sym, ConValue>,
|
||||||
|
mut patterns: &'pat [Pattern],
|
||||||
|
mut head: usize,
|
||||||
|
mut tail: usize,
|
||||||
|
) -> IResult<Option<(&'pat Pattern, usize, usize)>> {
|
||||||
|
// Bind the head of the list
|
||||||
|
while let [pattern, pat_tail @ ..] = patterns {
|
||||||
|
if matches!(pattern, Pattern::Rest(_)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if head >= tail {
|
||||||
|
return Err(Error::PatFailed(Box::new(pattern.clone())));
|
||||||
|
}
|
||||||
|
|
||||||
|
append_sub(env, sub, pattern, ConValue::Ref(head))?;
|
||||||
|
head += 1;
|
||||||
|
patterns = pat_tail;
|
||||||
|
}
|
||||||
|
// Bind the tail of the list
|
||||||
|
while let [pat_head @ .., pattern] = patterns {
|
||||||
|
if matches!(pattern, Pattern::Rest(_)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if head >= tail {
|
||||||
|
return Err(Error::PatFailed(Box::new(pattern.clone())));
|
||||||
|
};
|
||||||
|
append_sub(env, sub, pattern, ConValue::Ref(tail))?;
|
||||||
|
tail -= 1;
|
||||||
|
patterns = pat_head;
|
||||||
|
}
|
||||||
|
// Bind the ..rest of the list
|
||||||
|
match (patterns, tail - head) {
|
||||||
|
([], 0) | ([Pattern::Rest(None)], _) => Ok(None),
|
||||||
|
([Pattern::Rest(Some(pattern))], _) => Ok(Some((pattern.as_ref(), head, tail))),
|
||||||
|
_ => Err(Error::PatFailed(Box::new(Pattern::Array(patterns.into())))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Appends a substitution to the provided table
|
||||||
|
pub fn append_sub(
|
||||||
|
env: &Environment,
|
||||||
|
sub: &mut HashMap<Sym, ConValue>,
|
||||||
|
pat: &Pattern,
|
||||||
|
value: ConValue,
|
||||||
|
) -> IResult<()> {
|
||||||
|
match (pat, value) {
|
||||||
|
(Pattern::Literal(Literal::Bool(a)), ConValue::Bool(b)) => {
|
||||||
|
(*a == b).then_some(()).ok_or(Error::NotAssignable())
|
||||||
|
}
|
||||||
|
(Pattern::Literal(Literal::Char(a)), ConValue::Char(b)) => {
|
||||||
|
(*a == b).then_some(()).ok_or(Error::NotAssignable())
|
||||||
|
}
|
||||||
|
(Pattern::Literal(Literal::Float(a)), ConValue::Float(b)) => (f64::from_bits(*a) == b)
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(Error::NotAssignable()),
|
||||||
|
(Pattern::Literal(Literal::Int(a)), ConValue::Int(b)) => {
|
||||||
|
(b == *a as _).then_some(()).ok_or(Error::NotAssignable())
|
||||||
|
}
|
||||||
|
(Pattern::Literal(Literal::String(a)), ConValue::Str(b)) => {
|
||||||
|
(*a == *b).then_some(()).ok_or(Error::NotAssignable())
|
||||||
|
}
|
||||||
|
(Pattern::Literal(Literal::String(a)), ConValue::String(b)) => {
|
||||||
|
(*a == *b).then_some(()).ok_or(Error::NotAssignable())
|
||||||
|
}
|
||||||
|
(Pattern::Literal(_), _) => Err(Error::NotAssignable()),
|
||||||
|
|
||||||
|
(Pattern::Rest(Some(pat)), value) => match (pat.as_ref(), value) {
|
||||||
|
(Pattern::Literal(Literal::Int(a)), ConValue::Int(b)) => {
|
||||||
|
(b < *a as _).then_some(()).ok_or(Error::NotAssignable())
|
||||||
|
}
|
||||||
|
(Pattern::Literal(Literal::Char(a)), ConValue::Char(b)) => {
|
||||||
|
(b < *a as _).then_some(()).ok_or(Error::NotAssignable())
|
||||||
|
}
|
||||||
|
(Pattern::Literal(Literal::Bool(a)), ConValue::Bool(b)) => {
|
||||||
|
(!b & *a).then_some(()).ok_or(Error::NotAssignable())
|
||||||
|
}
|
||||||
|
(Pattern::Literal(Literal::Float(a)), ConValue::Float(b)) => {
|
||||||
|
(b < *a as _).then_some(()).ok_or(Error::NotAssignable())
|
||||||
|
}
|
||||||
|
(Pattern::Literal(Literal::String(a)), ConValue::Str(b)) => {
|
||||||
|
(&*b < a).then_some(()).ok_or(Error::NotAssignable())
|
||||||
|
}
|
||||||
|
(Pattern::Literal(Literal::String(a)), ConValue::String(b)) => {
|
||||||
|
(&*b < a).then_some(()).ok_or(Error::NotAssignable())
|
||||||
|
}
|
||||||
|
_ => Err(Error::NotAssignable()),
|
||||||
|
},
|
||||||
|
|
||||||
|
(Pattern::Name(name), _) if "_".eq(&**name) => Ok(()),
|
||||||
|
(Pattern::Name(name), value) => {
|
||||||
|
sub.insert(*name, value);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
(Pattern::Ref(_, pat), ConValue::Ref(r)) => match env.get_id(r) {
|
||||||
|
Some(value) => append_sub(env, sub, pat, value.clone()),
|
||||||
|
None => Err(Error::PatFailed(pat.clone())),
|
||||||
|
},
|
||||||
|
|
||||||
|
(Pattern::Ref(_, pat), ConValue::Slice(head, len)) => {
|
||||||
|
let mut values = Vec::with_capacity(len);
|
||||||
|
for idx in head..(head + len) {
|
||||||
|
values.push(env.get_id(idx).cloned().ok_or(Error::StackOverflow(idx))?);
|
||||||
|
}
|
||||||
|
append_sub(env, sub, pat, ConValue::Array(values.into_boxed_slice()))
|
||||||
|
}
|
||||||
|
|
||||||
|
(Pattern::RangeExc(head, tail), value) => match (head.as_ref(), tail.as_ref(), value) {
|
||||||
|
(
|
||||||
|
Pattern::Literal(Literal::Int(a)),
|
||||||
|
Pattern::Literal(Literal::Int(c)),
|
||||||
|
ConValue::Int(b),
|
||||||
|
) => (*a as isize <= b as _ && b < *c as isize)
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(Error::NotAssignable()),
|
||||||
|
(
|
||||||
|
Pattern::Literal(Literal::Char(a)),
|
||||||
|
Pattern::Literal(Literal::Char(c)),
|
||||||
|
ConValue::Char(b),
|
||||||
|
) => (*a <= b && b < *c)
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(Error::NotAssignable()),
|
||||||
|
(
|
||||||
|
Pattern::Literal(Literal::Float(a)),
|
||||||
|
Pattern::Literal(Literal::Float(c)),
|
||||||
|
ConValue::Float(b),
|
||||||
|
) => (f64::from_bits(*a) <= b && b < f64::from_bits(*c))
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(Error::NotAssignable()),
|
||||||
|
(
|
||||||
|
Pattern::Literal(Literal::String(a)),
|
||||||
|
Pattern::Literal(Literal::String(c)),
|
||||||
|
ConValue::Str(b),
|
||||||
|
) => (a.as_str() <= b.to_ref() && b.to_ref() < c.as_str())
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(Error::NotAssignable()),
|
||||||
|
(
|
||||||
|
Pattern::Literal(Literal::String(a)),
|
||||||
|
Pattern::Literal(Literal::String(c)),
|
||||||
|
ConValue::String(b),
|
||||||
|
) => (a.as_str() <= b.as_str() && b.as_str() < c.as_str())
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(Error::NotAssignable()),
|
||||||
|
_ => Err(Error::NotAssignable()),
|
||||||
|
},
|
||||||
|
|
||||||
|
(Pattern::RangeInc(head, tail), value) => match (head.as_ref(), tail.as_ref(), value) {
|
||||||
|
(
|
||||||
|
Pattern::Literal(Literal::Int(a)),
|
||||||
|
Pattern::Literal(Literal::Int(c)),
|
||||||
|
ConValue::Int(b),
|
||||||
|
) => (*a as isize <= b && b <= *c as isize)
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(Error::NotAssignable()),
|
||||||
|
(
|
||||||
|
Pattern::Literal(Literal::Char(a)),
|
||||||
|
Pattern::Literal(Literal::Char(c)),
|
||||||
|
ConValue::Char(b),
|
||||||
|
) => (*a <= b && b <= *c)
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(Error::NotAssignable()),
|
||||||
|
(
|
||||||
|
Pattern::Literal(Literal::Float(a)),
|
||||||
|
Pattern::Literal(Literal::Float(c)),
|
||||||
|
ConValue::Float(b),
|
||||||
|
) => (f64::from_bits(*a) <= b && b <= f64::from_bits(*c))
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(Error::NotAssignable()),
|
||||||
|
(
|
||||||
|
Pattern::Literal(Literal::String(a)),
|
||||||
|
Pattern::Literal(Literal::String(c)),
|
||||||
|
ConValue::Str(b),
|
||||||
|
) => (a.as_str() <= b.to_ref() && b.to_ref() <= c.as_str())
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(Error::NotAssignable()),
|
||||||
|
(
|
||||||
|
Pattern::Literal(Literal::String(a)),
|
||||||
|
Pattern::Literal(Literal::String(c)),
|
||||||
|
ConValue::String(b),
|
||||||
|
) => (a.as_str() <= b.as_str() && b.as_str() <= c.as_str())
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(Error::NotAssignable()),
|
||||||
|
_ => Err(Error::NotAssignable()),
|
||||||
|
},
|
||||||
|
|
||||||
|
(Pattern::Array(patterns), ConValue::Array(values)) => {
|
||||||
|
match rest_binding(env, sub, patterns, values.into_vec().into())? {
|
||||||
|
Some((pattern, values)) => {
|
||||||
|
append_sub(env, sub, pattern, ConValue::Array(Vec::from(values).into()))
|
||||||
|
}
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(Pattern::Array(patterns), ConValue::Slice(head, len)) => {
|
||||||
|
match rest_binding_ref(env, sub, patterns, head, head + len)? {
|
||||||
|
Some((pat, head, tail)) => {
|
||||||
|
append_sub(env, sub, pat, ConValue::Slice(head, tail - head))
|
||||||
|
}
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(Pattern::Tuple(patterns), ConValue::Empty) if patterns.is_empty() => Ok(()),
|
||||||
|
(Pattern::Tuple(patterns), ConValue::Tuple(values)) => {
|
||||||
|
match rest_binding(env, sub, patterns, values.into_vec().into())? {
|
||||||
|
Some((pattern, values)) => {
|
||||||
|
append_sub(env, sub, pattern, ConValue::Tuple(Vec::from(values).into()))
|
||||||
|
}
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(Pattern::TupleStruct(path, patterns), ConValue::TupleStruct(id, values)) => {
|
||||||
|
let tid = path
|
||||||
|
.as_sym()
|
||||||
|
.ok_or_else(|| Error::PatFailed(pat.clone().into()))?;
|
||||||
|
if id != tid {
|
||||||
|
return Err(Error::PatFailed(pat.clone().into()));
|
||||||
|
}
|
||||||
|
match rest_binding(env, sub, patterns, values.into_vec().into())? {
|
||||||
|
Some((pattern, values)) => {
|
||||||
|
append_sub(env, sub, pattern, ConValue::Tuple(Vec::from(values).into()))
|
||||||
|
}
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(Pattern::Struct(path, patterns), ConValue::Struct(id, mut values)) => {
|
||||||
|
let tid = path
|
||||||
|
.as_sym()
|
||||||
|
.ok_or_else(|| Error::PatFailed(pat.clone().into()))?;
|
||||||
|
if id != tid {
|
||||||
|
return Err(Error::PatFailed(pat.clone().into()));
|
||||||
|
}
|
||||||
|
for (name, pat) in patterns {
|
||||||
|
let value = values.remove(name).ok_or(Error::TypeError())?;
|
||||||
|
match pat {
|
||||||
|
Some(pat) => append_sub(env, sub, pat, value)?,
|
||||||
|
None => {
|
||||||
|
sub.insert(*name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
// eprintln!("Could not match pattern `{pat}` with value `{value}`!");
|
||||||
|
Err(Error::NotAssignable())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a substitution from a pattern and a value
|
||||||
|
pub fn substitution(
|
||||||
|
env: &Environment,
|
||||||
|
pat: &Pattern,
|
||||||
|
value: ConValue,
|
||||||
|
) -> IResult<HashMap<Sym, ConValue>> {
|
||||||
|
let mut sub = HashMap::new();
|
||||||
|
append_sub(env, &mut sub, pat, value)?;
|
||||||
|
Ok(sub)
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#![allow(unused_imports)]
|
#![allow(unused_imports)]
|
||||||
use crate::{env::Environment, temp_type_impl::ConValue, Interpret};
|
use crate::{Interpret, convalue::ConValue, env::Environment};
|
||||||
use cl_ast::*;
|
use cl_ast::*;
|
||||||
use cl_lexer::Lexer;
|
use cl_lexer::Lexer;
|
||||||
use cl_parser::Parser;
|
use cl_parser::Parser;
|
||||||
@@ -48,6 +48,7 @@ mod macros {
|
|||||||
//! ```
|
//! ```
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
use crate::IResult;
|
use crate::IResult;
|
||||||
|
use cl_parser::parser::Parse;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@@ -63,14 +64,14 @@ mod macros {
|
|||||||
///
|
///
|
||||||
/// Returns a `Result<`[`File`]`, ParseError>`
|
/// Returns a `Result<`[`File`]`, ParseError>`
|
||||||
pub macro file($($t:tt)*) {
|
pub macro file($($t:tt)*) {
|
||||||
Parser::new(Lexer::new(stringify!( $($t)* ))).file()
|
File::parse(&mut Parser::new(Lexer::new(stringify!( $($t)* ))))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stringifies, lexes, and parses everything you give to it
|
/// Stringifies, lexes, and parses everything you give to it
|
||||||
///
|
///
|
||||||
/// Returns a `Result<`[`Block`]`, ParseError>`
|
/// Returns a `Result<`[`Block`]`, ParseError>`
|
||||||
pub macro block($($t:tt)*) {
|
pub macro block($($t:tt)*) {
|
||||||
Parser::new(Lexer::new(stringify!({ $($t)* }))).block()
|
Block::parse(&mut Parser::new("test", Lexer::new(stringify!({ $($t)* }))))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluates a block of code in the given environment
|
/// Evaluates a block of code in the given environment
|
||||||
@@ -127,7 +128,7 @@ mod macros {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub macro env_ne($env:ident.$var:ident, $expr:expr) {{
|
pub macro env_ne($env:ident.$var:ident, $expr:expr) {{
|
||||||
let evaluated = $env.get(stringify!($var))
|
let evaluated = $env.get(stringify!($var).into())
|
||||||
.expect(stringify!($var should be defined and initialized));
|
.expect(stringify!($var should be defined and initialized));
|
||||||
if !conv_cmp!(neq, evaluated, $expr) {
|
if !conv_cmp!(neq, evaluated, $expr) {
|
||||||
panic!("assertion {} ({evaluated}) != {} failed.", stringify!($var), stringify!($expr))
|
panic!("assertion {} ({evaluated}) != {} failed.", stringify!($var), stringify!($expr))
|
||||||
@@ -135,7 +136,7 @@ mod macros {
|
|||||||
}}
|
}}
|
||||||
|
|
||||||
pub macro env_eq($env:ident.$var:ident, $expr:expr) {{
|
pub macro env_eq($env:ident.$var:ident, $expr:expr) {{
|
||||||
let evaluated = $env.get(stringify!($var))
|
let evaluated = $env.get(stringify!($var).into())
|
||||||
.expect(stringify!($var should be defined and initialized));
|
.expect(stringify!($var should be defined and initialized));
|
||||||
if !conv_cmp!(eq, evaluated, $expr) {
|
if !conv_cmp!(eq, evaluated, $expr) {
|
||||||
panic!("assertion {} ({evaluated}) == {} failed.", stringify!($var), stringify!($expr))
|
panic!("assertion {} ({evaluated}) == {} failed.", stringify!($var), stringify!($expr))
|
||||||
@@ -177,6 +178,45 @@ mod let_declarations {
|
|||||||
env_eq!(env.x, 10);
|
env_eq!(env.x, 10);
|
||||||
env_eq!(env.y, 10);
|
env_eq!(env.y, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn let_destructuring_tuple() {
|
||||||
|
let mut env = Environment::new();
|
||||||
|
assert_eval!(env,
|
||||||
|
let (x, y) = (10, 20);
|
||||||
|
);
|
||||||
|
|
||||||
|
env_eq!(env.x, 10);
|
||||||
|
env_eq!(env.y, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn let_destructuring_array() {
|
||||||
|
let mut env = Environment::new();
|
||||||
|
assert_eval!(env,
|
||||||
|
let [x, y] = [10, 20];
|
||||||
|
);
|
||||||
|
|
||||||
|
env_eq!(env.x, 10);
|
||||||
|
env_eq!(env.y, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn let_destructuring_nested() {
|
||||||
|
let mut env = Environment::new();
|
||||||
|
assert_eval!(env,
|
||||||
|
let (x, [one, two, three], (a, b, c))
|
||||||
|
= ('x', [1, 2, 3], ('a', 'b', 'c'));
|
||||||
|
);
|
||||||
|
|
||||||
|
env_eq!(env.x, 'x');
|
||||||
|
env_eq!(env.one, 1);
|
||||||
|
env_eq!(env.two, 2);
|
||||||
|
env_eq!(env.three, 3);
|
||||||
|
env_eq!(env.a, 'a');
|
||||||
|
env_eq!(env.b, 'b');
|
||||||
|
env_eq!(env.c, 'c');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod fn_declarations {
|
mod fn_declarations {
|
||||||
@@ -187,10 +227,10 @@ mod fn_declarations {
|
|||||||
assert_eval!(env, fn empty_fn() {});
|
assert_eval!(env, fn empty_fn() {});
|
||||||
// TODO: true equality for functions
|
// TODO: true equality for functions
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"fn empty_fn () {\n \n}",
|
"fn empty_fn () {}",
|
||||||
format!(
|
format!(
|
||||||
"{}",
|
"{}",
|
||||||
env.get("empty_fn")
|
env.get("empty_fn".into())
|
||||||
.expect(stringify!(empty_fn should be defined and initialized))
|
.expect(stringify!(empty_fn should be defined and initialized))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -363,7 +403,7 @@ mod operators {
|
|||||||
env_eq!(env.is_10_ne_20, true); // !=
|
env_eq!(env.is_10_ne_20, true); // !=
|
||||||
env_eq!(env.is_10_ge_20, false); // >=
|
env_eq!(env.is_10_ge_20, false); // >=
|
||||||
env_eq!(env.is_10_gt_20, false); // >
|
env_eq!(env.is_10_gt_20, false); // >
|
||||||
// Equal to
|
// Equal to
|
||||||
env_eq!(env.is_10_lt_10, false);
|
env_eq!(env.is_10_lt_10, false);
|
||||||
env_eq!(env.is_10_le_10, true);
|
env_eq!(env.is_10_le_10, true);
|
||||||
env_eq!(env.is_10_eq_10, true);
|
env_eq!(env.is_10_eq_10, true);
|
||||||
@@ -436,16 +476,17 @@ mod operators {
|
|||||||
env_eq!(env.y, 10);
|
env_eq!(env.y, 10);
|
||||||
env_eq!(env.z, 10);
|
env_eq!(env.z, 10);
|
||||||
}
|
}
|
||||||
#[test]
|
// Test is disabled, since new assignment system intentionally does not care.
|
||||||
#[should_panic]
|
// #[test]
|
||||||
fn assignment_accounts_for_type() {
|
// #[should_panic]
|
||||||
let mut env = Default::default();
|
// fn assignment_accounts_for_type() {
|
||||||
assert_eval!(env,
|
// let mut env = Default::default();
|
||||||
let x = "a string";
|
// assert_eval!(env,
|
||||||
let y = 0xdeadbeef;
|
// let x = "a string";
|
||||||
y = x; // should crash: type error
|
// let y = 0xdeadbeef;
|
||||||
);
|
// y = x; // should crash: type error
|
||||||
}
|
// );
|
||||||
|
// }
|
||||||
#[test]
|
#[test]
|
||||||
fn precedence() {
|
fn precedence() {
|
||||||
let mut env = Default::default();
|
let mut env = Default::default();
|
||||||
@@ -468,6 +509,75 @@ mod operators {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod control_flow {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn if_evaluates_pass_block_on_true() {
|
||||||
|
let mut env = Default::default();
|
||||||
|
assert_eval!(env,
|
||||||
|
let evaluated = if true { "pass" } else { "fail" }
|
||||||
|
);
|
||||||
|
env_eq!(env.evaluated, "pass");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn if_evaluates_fail_block_on_false() {
|
||||||
|
let mut env = Default::default();
|
||||||
|
assert_eval!(env,
|
||||||
|
let evaluated = if false { "pass" } else { "fail" }
|
||||||
|
);
|
||||||
|
env_eq!(env.evaluated, "fail");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn while_evaluates_fail_block_on_false() {
|
||||||
|
let mut env = Default::default();
|
||||||
|
assert_eval!(env,
|
||||||
|
let cond = true;
|
||||||
|
let evaluated = while cond { cond = false } else { true }
|
||||||
|
);
|
||||||
|
env_eq!(env.evaluated, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn while_does_not_evaluate_fail_block_on_break() {
|
||||||
|
let mut env = Default::default();
|
||||||
|
assert_eval!(env,
|
||||||
|
let evaluated = while true { break true } else { false }
|
||||||
|
);
|
||||||
|
env_eq!(env.evaluated, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn match_evaluates_in_order() {
|
||||||
|
let mut env = Default::default();
|
||||||
|
assert_eval!(env,
|
||||||
|
let x = '\u{1f988}';
|
||||||
|
let passed = match x {
|
||||||
|
'\u{1f988}' => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
);
|
||||||
|
env_eq!(env.passed, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn match_sinkoles_underscore_patterns() {
|
||||||
|
let mut env = Default::default();
|
||||||
|
assert_eval!(env,
|
||||||
|
let x = '\u{1f988}';
|
||||||
|
let passed = match x {
|
||||||
|
_ => true,
|
||||||
|
'\u{1f988}' => false,
|
||||||
|
};
|
||||||
|
);
|
||||||
|
env_eq!(env.passed, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: test other control flow constructs like loops, while-else, etc.
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn test_template() {
|
fn test_template() {
|
||||||
let mut env = Default::default();
|
let mut env = Default::default();
|
||||||
522
compiler/cl-lexer/src/lib.rs
Normal file
522
compiler/cl-lexer/src/lib.rs
Normal file
@@ -0,0 +1,522 @@
|
|||||||
|
//! Converts a text file into tokens
|
||||||
|
#![warn(clippy::all)]
|
||||||
|
#![feature(decl_macro)]
|
||||||
|
use cl_structures::span::Loc;
|
||||||
|
use cl_token::{TokenKind as Kind, *};
|
||||||
|
use std::{
|
||||||
|
iter::Peekable,
|
||||||
|
str::{CharIndices, FromStr},
|
||||||
|
};
|
||||||
|
use unicode_ident::*;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
pub mod lexer_iter {
|
||||||
|
//! Iterator over a [`Lexer`], returning [`LResult<Token>`]s
|
||||||
|
use super::{
|
||||||
|
Lexer, Token,
|
||||||
|
error::{LResult, Reason},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Iterator over a [`Lexer`], returning [`LResult<Token>`]s
|
||||||
|
pub struct LexerIter<'t> {
|
||||||
|
lexer: Lexer<'t>,
|
||||||
|
}
|
||||||
|
impl Iterator for LexerIter<'_> {
|
||||||
|
type Item = LResult<Token>;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
match self.lexer.scan() {
|
||||||
|
Ok(v) => Some(Ok(v)),
|
||||||
|
Err(e) => {
|
||||||
|
if e.reason == Reason::EndOfFile {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Err(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'t> IntoIterator for Lexer<'t> {
|
||||||
|
type Item = LResult<Token>;
|
||||||
|
type IntoIter = LexerIter<'t>;
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
LexerIter { lexer: self }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Lexer iterates over the characters in a body of text, searching for [Tokens](Token).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use cl_lexer::Lexer;
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// // Read in your code from somewhere
|
||||||
|
/// let some_code = "
|
||||||
|
/// fn main () {
|
||||||
|
/// // TODO: code goes here!
|
||||||
|
/// }
|
||||||
|
/// ";
|
||||||
|
/// // Create a lexer over your code
|
||||||
|
/// let mut lexer = Lexer::new(some_code);
|
||||||
|
/// // Scan for a single token
|
||||||
|
/// let first_token = lexer.scan()?;
|
||||||
|
/// println!("{first_token:?}");
|
||||||
|
/// // Loop over all the rest of the tokens
|
||||||
|
/// for token in lexer {
|
||||||
|
/// # let token: Result<_,()> = Ok(token?);
|
||||||
|
/// match token {
|
||||||
|
/// Ok(token) => println!("{token:?}"),
|
||||||
|
/// Err(e) => eprintln!("{e:?}"),
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// # Ok(()) }
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Lexer<'t> {
|
||||||
|
/// The source text
|
||||||
|
text: &'t str,
|
||||||
|
/// A peekable iterator over the source text
|
||||||
|
iter: Peekable<CharIndices<'t>>,
|
||||||
|
/// The end of the current token
|
||||||
|
head: usize,
|
||||||
|
/// The (line, col) end of the current token
|
||||||
|
head_loc: (u32, u32),
|
||||||
|
/// The start of the current token
|
||||||
|
tail: usize,
|
||||||
|
/// The (line, col) start of the current token
|
||||||
|
tail_loc: (u32, u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'t> Lexer<'t> {
|
||||||
|
/// Creates a new [Lexer] over a [str]
|
||||||
|
pub fn new(text: &'t str) -> Self {
|
||||||
|
Self {
|
||||||
|
text,
|
||||||
|
iter: text.char_indices().peekable(),
|
||||||
|
head: 0,
|
||||||
|
head_loc: (1, 1),
|
||||||
|
tail: 0,
|
||||||
|
tail_loc: (1, 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the current line
|
||||||
|
pub fn line(&self) -> u32 {
|
||||||
|
self.tail_loc.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the current column
|
||||||
|
pub fn col(&self) -> u32 {
|
||||||
|
self.tail_loc.1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the current token's lexeme
|
||||||
|
fn lexeme(&mut self) -> &'t str {
|
||||||
|
&self.text[self.tail..self.head]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Peeks the next character without advancing the lexer
|
||||||
|
fn peek(&mut self) -> Option<char> {
|
||||||
|
self.iter.peek().map(|(_, c)| *c)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Advances the 'tail' (current position)
|
||||||
|
fn advance_tail(&mut self) {
|
||||||
|
let (idx, c) = self.iter.peek().copied().unwrap_or((self.text.len(), '\0'));
|
||||||
|
let (line, col) = &mut self.head_loc;
|
||||||
|
let diff = idx - self.head;
|
||||||
|
|
||||||
|
self.head = idx;
|
||||||
|
match c {
|
||||||
|
'\n' => {
|
||||||
|
*line += 1;
|
||||||
|
*col = 1;
|
||||||
|
}
|
||||||
|
_ => *col += diff as u32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes the last-peeked character, or the next character if none peeked.
|
||||||
|
pub fn take(&mut self) -> Option<char> {
|
||||||
|
let (_, c) = self.iter.next()?;
|
||||||
|
self.advance_tail();
|
||||||
|
Some(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes the next char if it matches the `expected` char
|
||||||
|
pub fn next_if(&mut self, expected: char) -> Option<char> {
|
||||||
|
let (_, c) = self.iter.next_if(|&(_, c)| c == expected)?;
|
||||||
|
self.advance_tail();
|
||||||
|
Some(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes the last-peeked character, advancing the tail
|
||||||
|
pub fn consume(&mut self) -> &mut Self {
|
||||||
|
self.iter.next();
|
||||||
|
self.advance_tail();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produces an [Error] at the start of the current token
|
||||||
|
fn error(&self, reason: Reason) -> Error {
|
||||||
|
Error { reason, line: self.line(), col: self.col() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produces a token with the current [lexeme](Lexer::lexeme) as its data
|
||||||
|
fn produce(&mut self, kind: Kind) -> LResult<Token> {
|
||||||
|
let lexeme = self.lexeme().to_owned();
|
||||||
|
self.produce_with(kind, lexeme)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produces a token with the provided `data`
|
||||||
|
fn produce_with(&mut self, kind: Kind, data: impl Into<TokenData>) -> LResult<Token> {
|
||||||
|
let loc = self.tail_loc;
|
||||||
|
self.tail_loc = self.head_loc;
|
||||||
|
self.tail = self.head;
|
||||||
|
Ok(Token::new(kind, data, loc.0, loc.1))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produces a token with no `data`
|
||||||
|
fn produce_op(&mut self, kind: Kind) -> LResult<Token> {
|
||||||
|
self.produce_with(kind, ())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes 0 or more whitespace
|
||||||
|
fn skip_whitespace(&mut self) -> &mut Self {
|
||||||
|
while self.peek().is_some_and(char::is_whitespace) {
|
||||||
|
let _ = self.consume();
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Starts a new token
|
||||||
|
fn start_token(&mut self) -> &mut Self {
|
||||||
|
self.tail_loc = self.head_loc;
|
||||||
|
self.tail = self.head;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scans through the text, searching for the next [Token]
|
||||||
|
pub fn scan(&mut self) -> LResult<Token> {
|
||||||
|
use TokenKind::*;
|
||||||
|
// !"#%&'()*+,-./:;<=>?@[\\]^`{|}~
|
||||||
|
let tok = match self
|
||||||
|
.skip_whitespace()
|
||||||
|
.start_token()
|
||||||
|
.peek()
|
||||||
|
.ok_or_else(|| self.error(Reason::EndOfFile))?
|
||||||
|
{
|
||||||
|
'!' => Bang,
|
||||||
|
'"' => return self.string(),
|
||||||
|
'#' => Hash,
|
||||||
|
'%' => Rem,
|
||||||
|
'&' => Amp,
|
||||||
|
'\'' => return self.character(),
|
||||||
|
'(' => LParen,
|
||||||
|
')' => RParen,
|
||||||
|
'*' => Star,
|
||||||
|
'+' => Plus,
|
||||||
|
',' => Comma,
|
||||||
|
'-' => Minus,
|
||||||
|
'.' => Dot,
|
||||||
|
'/' => Slash,
|
||||||
|
'0' => TokenKind::Literal,
|
||||||
|
'1'..='9' => return self.digits::<10>(),
|
||||||
|
':' => Colon,
|
||||||
|
';' => Semi,
|
||||||
|
'<' => Lt,
|
||||||
|
'=' => Eq,
|
||||||
|
'>' => Gt,
|
||||||
|
'?' => Question,
|
||||||
|
'@' => At,
|
||||||
|
'[' => LBrack,
|
||||||
|
'\\' => Backslash,
|
||||||
|
']' => RBrack,
|
||||||
|
'^' => Xor,
|
||||||
|
'`' => Grave,
|
||||||
|
'{' => LCurly,
|
||||||
|
'|' => Bar,
|
||||||
|
'}' => RCurly,
|
||||||
|
'~' => Tilde,
|
||||||
|
'_' => return self.identifier(),
|
||||||
|
c if is_xid_start(c) => return self.identifier(),
|
||||||
|
e => {
|
||||||
|
let err = Err(self.error(Reason::UnexpectedChar(e)));
|
||||||
|
let _ = self.consume();
|
||||||
|
err?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle digraphs
|
||||||
|
let tok = match (tok, self.consume().peek()) {
|
||||||
|
(Literal, Some('b')) => return self.consume().digits::<2>(),
|
||||||
|
(Literal, Some('d')) => return self.consume().digits::<10>(),
|
||||||
|
(Literal, Some('o')) => return self.consume().digits::<8>(),
|
||||||
|
(Literal, Some('x')) => return self.consume().digits::<16>(),
|
||||||
|
(Literal, Some('~')) => return self.consume().digits::<36>(),
|
||||||
|
(Literal, _) => return self.digits::<10>(),
|
||||||
|
(Amp, Some('&')) => AmpAmp,
|
||||||
|
(Amp, Some('=')) => AmpEq,
|
||||||
|
(Bang, Some('!')) => BangBang,
|
||||||
|
(Bang, Some('=')) => BangEq,
|
||||||
|
(Bar, Some('|')) => BarBar,
|
||||||
|
(Bar, Some('=')) => BarEq,
|
||||||
|
(Colon, Some(':')) => ColonColon,
|
||||||
|
(Dot, Some('.')) => DotDot,
|
||||||
|
(Eq, Some('=')) => EqEq,
|
||||||
|
(Eq, Some('>')) => FatArrow,
|
||||||
|
(Gt, Some('=')) => GtEq,
|
||||||
|
(Gt, Some('>')) => GtGt,
|
||||||
|
(Hash, Some('!')) => HashBang,
|
||||||
|
(Lt, Some('=')) => LtEq,
|
||||||
|
(Lt, Some('<')) => LtLt,
|
||||||
|
(Minus, Some('=')) => MinusEq,
|
||||||
|
(Minus, Some('>')) => Arrow,
|
||||||
|
(Plus, Some('=')) => PlusEq,
|
||||||
|
(Rem, Some('=')) => RemEq,
|
||||||
|
(Slash, Some('*')) => return self.block_comment()?.produce(Kind::Comment),
|
||||||
|
(Slash, Some('/')) => return self.line_comment(),
|
||||||
|
(Slash, Some('=')) => SlashEq,
|
||||||
|
(Star, Some('=')) => StarEq,
|
||||||
|
(Xor, Some('=')) => XorEq,
|
||||||
|
(Xor, Some('^')) => XorXor,
|
||||||
|
_ => return self.produce_op(tok),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle trigraphs
|
||||||
|
let tok = match (tok, self.consume().peek()) {
|
||||||
|
(HashBang, Some('/')) => return self.line_comment(),
|
||||||
|
(DotDot, Some('=')) => DotDotEq,
|
||||||
|
(GtGt, Some('=')) => GtGtEq,
|
||||||
|
(LtLt, Some('=')) => LtLtEq,
|
||||||
|
_ => return self.produce_op(tok),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.consume().produce_op(tok)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Comments
|
||||||
|
impl Lexer<'_> {
|
||||||
|
/// Consumes until the next newline '\n', producing a [Comment](Kind::Comment)
|
||||||
|
fn line_comment(&mut self) -> LResult<Token> {
|
||||||
|
while self.consume().peek().is_some_and(|c| c != '\n') {}
|
||||||
|
self.produce(Kind::Comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes nested block-comments. Does not produce by itself.
|
||||||
|
fn block_comment(&mut self) -> LResult<&mut Self> {
|
||||||
|
self.consume();
|
||||||
|
while let Some(c) = self.take() {
|
||||||
|
match (c, self.peek()) {
|
||||||
|
('/', Some('*')) => self.block_comment()?,
|
||||||
|
('*', Some('/')) => return Ok(self.consume()),
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Err(self.error(Reason::UnmatchedDelimiters('/')))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Identifiers
|
||||||
|
impl Lexer<'_> {
|
||||||
|
/// Produces an [Identifier](Kind::Identifier) or keyword
|
||||||
|
fn identifier(&mut self) -> LResult<Token> {
|
||||||
|
while self.consume().peek().is_some_and(is_xid_continue) {}
|
||||||
|
if let Ok(keyword) = Kind::from_str(self.lexeme()) {
|
||||||
|
self.produce_with(keyword, ())
|
||||||
|
} else {
|
||||||
|
self.produce(Kind::Identifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Integers
|
||||||
|
impl Lexer<'_> {
|
||||||
|
/// Produces a [Literal](Kind::Literal) with an integer or float value.
|
||||||
|
fn digits<const B: u32>(&mut self) -> LResult<Token> {
|
||||||
|
let mut value = 0;
|
||||||
|
while let Some(true) = self.peek().as_ref().map(char::is_ascii_alphanumeric) {
|
||||||
|
value = value * B as u128 + self.digit::<B>()? as u128;
|
||||||
|
}
|
||||||
|
// TODO: find a better way to handle floats in the tokenizer
|
||||||
|
match self.peek() {
|
||||||
|
Some('.') => {
|
||||||
|
// FIXME: hack: 0.. is not [0.0, '.']
|
||||||
|
if let Some('.') = self.clone().consume().take() {
|
||||||
|
return self.produce_with(Kind::Literal, value);
|
||||||
|
}
|
||||||
|
let mut float = format!("{value}.");
|
||||||
|
self.consume();
|
||||||
|
while let Some(true) = self.peek().as_ref().map(char::is_ascii_digit) {
|
||||||
|
float.push(self.iter.next().map(|(_, c)| c).unwrap_or_default());
|
||||||
|
}
|
||||||
|
let float = f64::from_str(&float).expect("must be parsable as float");
|
||||||
|
self.produce_with(Kind::Literal, float)
|
||||||
|
}
|
||||||
|
_ => self.produce_with(Kind::Literal, value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes a single digit of base [B](Lexer::digit)
|
||||||
|
fn digit<const B: u32>(&mut self) -> LResult<u32> {
|
||||||
|
let digit = self.take().ok_or_else(|| self.error(Reason::EndOfFile))?;
|
||||||
|
digit
|
||||||
|
.to_digit(B)
|
||||||
|
.ok_or_else(|| self.error(Reason::InvalidDigit(digit)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Strings and characters
|
||||||
|
impl Lexer<'_> {
|
||||||
|
/// Produces a [Literal](Kind::Literal) with a pre-escaped [String]
|
||||||
|
pub fn string(&mut self) -> Result<Token, Error> {
|
||||||
|
let mut lexeme = String::new();
|
||||||
|
let mut depth = 0;
|
||||||
|
self.consume();
|
||||||
|
loop {
|
||||||
|
lexeme.push(match self.take() {
|
||||||
|
None => Err(self.error(Reason::UnmatchedDelimiters('"')))?,
|
||||||
|
Some('\\') => self.unescape()?,
|
||||||
|
Some('"') if depth == 0 => break,
|
||||||
|
Some(c @ '{') => {
|
||||||
|
depth += 1;
|
||||||
|
c
|
||||||
|
}
|
||||||
|
Some(c @ '}') => {
|
||||||
|
depth -= 1;
|
||||||
|
c
|
||||||
|
}
|
||||||
|
Some(c) => c,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
lexeme.shrink_to_fit();
|
||||||
|
self.produce_with(Kind::Literal, lexeme)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produces a [Literal](Kind::Literal) with a pre-escaped [char]
|
||||||
|
fn character(&mut self) -> Result<Token, Error> {
|
||||||
|
let c = match self.consume().take() {
|
||||||
|
Some('\\') => self.unescape()?,
|
||||||
|
Some(c) => c,
|
||||||
|
None => '\0',
|
||||||
|
};
|
||||||
|
if self.take().is_some_and(|c| c == '\'') {
|
||||||
|
self.produce_with(Kind::Literal, c)
|
||||||
|
} else {
|
||||||
|
Err(self.error(Reason::UnmatchedDelimiters('\'')))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unescapes a single character
|
||||||
|
#[rustfmt::skip]
|
||||||
|
fn unescape(&mut self) -> LResult<char> {
|
||||||
|
Ok(match self.take().ok_or_else(|| self.error(Reason::EndOfFile))? {
|
||||||
|
' ' => '\u{a0}',
|
||||||
|
'0' => '\0',
|
||||||
|
'a' => '\x07',
|
||||||
|
'b' => '\x08',
|
||||||
|
'e' => '\x1b',
|
||||||
|
'f' => '\x0c',
|
||||||
|
'n' => '\n',
|
||||||
|
'r' => '\r',
|
||||||
|
't' => '\t',
|
||||||
|
'u' => self.unicode_escape()?,
|
||||||
|
'x' => self.hex_escape()?,
|
||||||
|
chr => chr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/// Unescapes a single 2-digit hex escape
|
||||||
|
fn hex_escape(&mut self) -> LResult<char> {
|
||||||
|
let out = (self.digit::<16>()? << 4) + self.digit::<16>()?;
|
||||||
|
char::from_u32(out).ok_or_else(|| self.error(Reason::BadUnicode(out)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unescapes a single \u{} unicode escape
|
||||||
|
pub fn unicode_escape(&mut self) -> Result<char, Error> {
|
||||||
|
self.next_if('{')
|
||||||
|
.ok_or_else(|| self.error(Reason::InvalidEscape('u')))?;
|
||||||
|
let mut out = 0;
|
||||||
|
while let Some(c) = self.take() {
|
||||||
|
if c == '}' {
|
||||||
|
return char::from_u32(out).ok_or_else(|| self.error(Reason::BadUnicode(out)));
|
||||||
|
}
|
||||||
|
out = out * 16
|
||||||
|
+ c.to_digit(16)
|
||||||
|
.ok_or_else(|| self.error(Reason::InvalidDigit(c)))?;
|
||||||
|
}
|
||||||
|
Err(self.error(Reason::UnmatchedDelimiters('}')))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'t> From<&Lexer<'t>> for Loc {
|
||||||
|
fn from(value: &Lexer<'t>) -> Self {
|
||||||
|
Loc(value.line(), value.col())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use error::{Error, LResult, Reason};
|
||||||
|
pub mod error {
|
||||||
|
//! [Error] type for the [Lexer](super::Lexer)
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
/// Result type with [Err] = [Error]
|
||||||
|
pub type LResult<T> = Result<T, Error>;
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct Error {
|
||||||
|
pub reason: Reason,
|
||||||
|
pub line: u32,
|
||||||
|
pub col: u32,
|
||||||
|
}
|
||||||
|
/// The reason for the [Error]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Reason {
|
||||||
|
/// Found an opening delimiter of type [char], but not the expected closing delimiter
|
||||||
|
UnmatchedDelimiters(char),
|
||||||
|
/// Found a character that doesn't belong to any [TokenKind](cl_token::TokenKind)
|
||||||
|
UnexpectedChar(char),
|
||||||
|
/// Found a character that's not valid in an escape sequence while looking for an escape
|
||||||
|
/// sequence
|
||||||
|
UnknownEscape(char),
|
||||||
|
/// Escape sequence contains invalid hexadecimal digit or unmatched braces
|
||||||
|
InvalidEscape(char),
|
||||||
|
/// Character is not a valid digit in the requested base
|
||||||
|
InvalidDigit(char),
|
||||||
|
/// Unicode escape does not map to a valid unicode code-point
|
||||||
|
BadUnicode(u32),
|
||||||
|
/// Reached end of input
|
||||||
|
EndOfFile,
|
||||||
|
}
|
||||||
|
impl Error {
|
||||||
|
/// Returns the [Reason] for this error
|
||||||
|
pub fn reason(&self) -> &Reason {
|
||||||
|
&self.reason
|
||||||
|
}
|
||||||
|
/// Returns the (line, col) where the error happened
|
||||||
|
pub fn location(&self) -> (u32, u32) {
|
||||||
|
(self.line, self.col)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::error::Error for Error {}
|
||||||
|
impl Display for Error {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}:{}: {}", self.line, self.col, self.reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Display for Reason {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Reason::UnmatchedDelimiters(c) => write! {f, "Unmatched `{c:?}` in input"},
|
||||||
|
Reason::UnexpectedChar(c) => write!(f, "Character `{c:?}` not expected"),
|
||||||
|
Reason::UnknownEscape(c) => write!(f, "`\\{c}` is not a known escape sequence"),
|
||||||
|
Reason::InvalidEscape(c) => write!(f, "Escape sequence `\\{c}`... is malformed"),
|
||||||
|
Reason::InvalidDigit(c) => write!(f, "`{c:?}` is not a valid digit"),
|
||||||
|
Reason::BadUnicode(c) => write!(f, "`\\u{{{c:x}}}` is not valid unicode"),
|
||||||
|
Reason::EndOfFile => write!(f, "Reached end of input"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -110,7 +110,7 @@ mod string {
|
|||||||
}
|
}
|
||||||
mod punct {
|
mod punct {
|
||||||
macro op($op:ident) {
|
macro op($op:ident) {
|
||||||
TokenKind::Punct(Punct::$op)
|
TokenKind::$op
|
||||||
}
|
}
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
use cl_ast::{Expr, Sym};
|
||||||
use cl_lexer::error::{Error as LexError, Reason};
|
use cl_lexer::error::{Error as LexError, Reason};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
pub type PResult<T> = Result<T, Error>;
|
pub type PResult<T> = Result<T, Error>;
|
||||||
@@ -7,6 +8,7 @@ pub type PResult<T> = Result<T, Error>;
|
|||||||
/// Contains information about [Parser] errors
|
/// Contains information about [Parser] errors
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
|
pub in_file: Sym,
|
||||||
pub reason: ErrorKind,
|
pub reason: ErrorKind,
|
||||||
pub while_parsing: Parsing,
|
pub while_parsing: Parsing,
|
||||||
pub loc: Loc,
|
pub loc: Loc,
|
||||||
@@ -22,14 +24,16 @@ pub enum ErrorKind {
|
|||||||
UnmatchedCurlyBraces,
|
UnmatchedCurlyBraces,
|
||||||
UnmatchedSquareBrackets,
|
UnmatchedSquareBrackets,
|
||||||
Unexpected(TokenKind),
|
Unexpected(TokenKind),
|
||||||
Expected {
|
ExpectedToken {
|
||||||
want: TokenKind,
|
want: TokenKind,
|
||||||
got: TokenKind,
|
got: TokenKind,
|
||||||
},
|
},
|
||||||
/// No rules matched
|
ExpectedParsing {
|
||||||
Nothing,
|
want: Parsing,
|
||||||
|
},
|
||||||
|
InvalidPattern(Box<Expr>),
|
||||||
/// Indicates unfinished code
|
/// Indicates unfinished code
|
||||||
Todo,
|
Todo(&'static str),
|
||||||
}
|
}
|
||||||
impl From<LexError> for ErrorKind {
|
impl From<LexError> for ErrorKind {
|
||||||
fn from(value: LexError) -> Self {
|
fn from(value: LexError) -> Self {
|
||||||
@@ -43,15 +47,20 @@ impl From<LexError> for ErrorKind {
|
|||||||
/// Compactly represents the stage of parsing an [Error] originated in
|
/// Compactly represents the stage of parsing an [Error] originated in
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum Parsing {
|
pub enum Parsing {
|
||||||
|
Mutability,
|
||||||
|
Visibility,
|
||||||
|
Identifier,
|
||||||
|
Literal,
|
||||||
|
|
||||||
File,
|
File,
|
||||||
|
|
||||||
Attrs,
|
Attrs,
|
||||||
Meta,
|
Meta,
|
||||||
|
MetaKind,
|
||||||
|
|
||||||
Item,
|
Item,
|
||||||
Visibility,
|
|
||||||
Mutability,
|
|
||||||
ItemKind,
|
ItemKind,
|
||||||
|
Generics,
|
||||||
Alias,
|
Alias,
|
||||||
Const,
|
Const,
|
||||||
Static,
|
Static,
|
||||||
@@ -67,38 +76,47 @@ pub enum Parsing {
|
|||||||
Variant,
|
Variant,
|
||||||
VariantKind,
|
VariantKind,
|
||||||
Impl,
|
Impl,
|
||||||
|
ImplKind,
|
||||||
|
Use,
|
||||||
|
UseTree,
|
||||||
|
|
||||||
Ty,
|
Ty,
|
||||||
TyKind,
|
TyKind,
|
||||||
|
TySlice,
|
||||||
|
TyArray,
|
||||||
TyTuple,
|
TyTuple,
|
||||||
TyRef,
|
TyRef,
|
||||||
TyFn,
|
TyFn,
|
||||||
|
|
||||||
|
Path,
|
||||||
|
PathPart,
|
||||||
|
|
||||||
Stmt,
|
Stmt,
|
||||||
StmtKind,
|
StmtKind,
|
||||||
Let,
|
Let,
|
||||||
|
|
||||||
Expr,
|
Expr,
|
||||||
ExprKind,
|
ExprKind,
|
||||||
|
Closure,
|
||||||
Assign,
|
Assign,
|
||||||
AssignKind,
|
AssignKind,
|
||||||
Binary,
|
Binary,
|
||||||
BinaryKind,
|
BinaryKind,
|
||||||
Unary,
|
Unary,
|
||||||
UnaryKind,
|
UnaryKind,
|
||||||
|
Cast,
|
||||||
Index,
|
Index,
|
||||||
|
Structor,
|
||||||
|
Fielder,
|
||||||
Call,
|
Call,
|
||||||
Member,
|
Member,
|
||||||
PathExpr,
|
|
||||||
PathPart,
|
|
||||||
Identifier,
|
|
||||||
Literal,
|
|
||||||
Array,
|
Array,
|
||||||
ArrayRep,
|
ArrayRep,
|
||||||
AddrOf,
|
AddrOf,
|
||||||
Block,
|
Block,
|
||||||
Group,
|
Group,
|
||||||
Tuple,
|
Tuple,
|
||||||
|
Loop,
|
||||||
While,
|
While,
|
||||||
If,
|
If,
|
||||||
For,
|
For,
|
||||||
@@ -106,17 +124,26 @@ pub enum Parsing {
|
|||||||
Break,
|
Break,
|
||||||
Return,
|
Return,
|
||||||
Continue,
|
Continue,
|
||||||
|
|
||||||
|
Pattern,
|
||||||
|
Match,
|
||||||
|
MatchArm,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Error {
|
impl Display for Error {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let Self { reason, while_parsing, loc } = self;
|
let Self { in_file, reason, while_parsing, loc } = self;
|
||||||
match reason {
|
match reason {
|
||||||
// TODO entries are debug-printed
|
// TODO entries are debug-printed
|
||||||
ErrorKind::Todo => write!(f, "{loc} {reason} {while_parsing:?}"),
|
ErrorKind::Todo(_) => write!(f, "{in_file}:{loc} {reason} {while_parsing:?}"),
|
||||||
// lexical errors print their own higher-resolution loc info
|
// lexical errors print their own higher-resolution loc info
|
||||||
ErrorKind::Lexical(e) => write!(f, "{e} (while parsing {while_parsing})"),
|
ErrorKind::Lexical(e) => write!(f, "{e} (while parsing {while_parsing})"),
|
||||||
_ => write!(f, "{loc} {reason} while parsing {while_parsing}"),
|
_ => {
|
||||||
|
if !in_file.is_empty() {
|
||||||
|
write!(f, "{in_file}:")?
|
||||||
|
}
|
||||||
|
write!(f, "{loc}: {reason} while parsing {while_parsing}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,26 +156,29 @@ impl Display for ErrorKind {
|
|||||||
ErrorKind::UnmatchedCurlyBraces => write!(f, "Unmatched curly braces"),
|
ErrorKind::UnmatchedCurlyBraces => write!(f, "Unmatched curly braces"),
|
||||||
ErrorKind::UnmatchedSquareBrackets => write!(f, "Unmatched square brackets"),
|
ErrorKind::UnmatchedSquareBrackets => write!(f, "Unmatched square brackets"),
|
||||||
ErrorKind::Unexpected(t) => write!(f, "Encountered unexpected token `{t}`"),
|
ErrorKind::Unexpected(t) => write!(f, "Encountered unexpected token `{t}`"),
|
||||||
ErrorKind::Expected { want: e, got: g } => {
|
ErrorKind::ExpectedToken { want: e, got: g } => write!(f, "Expected `{e}`, got `{g}`"),
|
||||||
write!(f, "Expected `{e}`, got `{g}`")
|
ErrorKind::ExpectedParsing { want } => write!(f, "Expected {want}"),
|
||||||
}
|
ErrorKind::InvalidPattern(got) => write!(f, "Got invalid `{got}`"),
|
||||||
ErrorKind::Nothing => write!(f, "Nothing found"),
|
ErrorKind::Todo(unfinished) => write!(f, "TODO: {unfinished}"),
|
||||||
ErrorKind::Todo => write!(f, "TODO:"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Display for Parsing {
|
impl Display for Parsing {
|
||||||
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 {
|
||||||
|
Parsing::Visibility => "a visibility qualifier",
|
||||||
|
Parsing::Mutability => "a mutability qualifier",
|
||||||
|
Parsing::Identifier => "an identifier",
|
||||||
|
Parsing::Literal => "a literal",
|
||||||
|
|
||||||
Parsing::File => "a file",
|
Parsing::File => "a file",
|
||||||
|
|
||||||
Parsing::Attrs => "an attribute-set",
|
Parsing::Attrs => "an attribute-set",
|
||||||
Parsing::Meta => "an attribute",
|
Parsing::Meta => "an attribute",
|
||||||
|
Parsing::MetaKind => "an attribute's arguments",
|
||||||
Parsing::Item => "an item",
|
Parsing::Item => "an item",
|
||||||
Parsing::Visibility => "a visibility qualifier",
|
|
||||||
Parsing::Mutability => "a mutability qualifier",
|
|
||||||
Parsing::ItemKind => "an item",
|
Parsing::ItemKind => "an item",
|
||||||
|
Parsing::Generics => "a list of type arguments",
|
||||||
Parsing::Alias => "a type alias",
|
Parsing::Alias => "a type alias",
|
||||||
Parsing::Const => "a const item",
|
Parsing::Const => "a const item",
|
||||||
Parsing::Static => "a static variable",
|
Parsing::Static => "a static variable",
|
||||||
@@ -164,38 +194,47 @@ impl Display for Parsing {
|
|||||||
Parsing::Variant => "an enum variant",
|
Parsing::Variant => "an enum variant",
|
||||||
Parsing::VariantKind => "an enum variant",
|
Parsing::VariantKind => "an enum variant",
|
||||||
Parsing::Impl => "an impl block",
|
Parsing::Impl => "an impl block",
|
||||||
|
Parsing::ImplKind => "the target of an impl block",
|
||||||
|
Parsing::Use => "a use item",
|
||||||
|
Parsing::UseTree => "a use-tree",
|
||||||
|
|
||||||
Parsing::Ty => "a type",
|
Parsing::Ty => "a type",
|
||||||
Parsing::TyKind => "a type",
|
Parsing::TyKind => "a type",
|
||||||
|
Parsing::TySlice => "a slice type",
|
||||||
|
Parsing::TyArray => "an array type",
|
||||||
Parsing::TyTuple => "a tuple of types",
|
Parsing::TyTuple => "a tuple of types",
|
||||||
Parsing::TyRef => "a reference type",
|
Parsing::TyRef => "a reference type",
|
||||||
Parsing::TyFn => "a function pointer type",
|
Parsing::TyFn => "a function pointer type",
|
||||||
|
|
||||||
|
Parsing::Path => "a path",
|
||||||
|
Parsing::PathPart => "a path component",
|
||||||
|
|
||||||
Parsing::Stmt => "a statement",
|
Parsing::Stmt => "a statement",
|
||||||
Parsing::StmtKind => "a statement",
|
Parsing::StmtKind => "a statement",
|
||||||
Parsing::Let => "a local variable declaration",
|
Parsing::Let => "a local variable declaration",
|
||||||
|
|
||||||
Parsing::Expr => "an expression",
|
Parsing::Expr => "an expression",
|
||||||
Parsing::ExprKind => "an expression",
|
Parsing::ExprKind => "an expression",
|
||||||
|
Parsing::Closure => "an anonymous function",
|
||||||
Parsing::Assign => "an assignment",
|
Parsing::Assign => "an assignment",
|
||||||
Parsing::AssignKind => "an assignment operator",
|
Parsing::AssignKind => "an assignment operator",
|
||||||
Parsing::Binary => "a binary expression",
|
Parsing::Binary => "a binary expression",
|
||||||
Parsing::BinaryKind => "a binary operator",
|
Parsing::BinaryKind => "a binary operator",
|
||||||
Parsing::Unary => "a unary expression",
|
Parsing::Unary => "a unary expression",
|
||||||
Parsing::UnaryKind => "a unary operator",
|
Parsing::UnaryKind => "a unary operator",
|
||||||
|
Parsing::Cast => "an `as`-casting expression",
|
||||||
Parsing::Index => "an indexing expression",
|
Parsing::Index => "an indexing expression",
|
||||||
|
Parsing::Structor => "a struct constructor expression",
|
||||||
|
Parsing::Fielder => "a struct field expression",
|
||||||
Parsing::Call => "a call expression",
|
Parsing::Call => "a call expression",
|
||||||
Parsing::Member => "a member access expression",
|
Parsing::Member => "a member access expression",
|
||||||
Parsing::PathExpr => "a path",
|
|
||||||
Parsing::PathPart => "a path component",
|
|
||||||
Parsing::Identifier => "an identifier",
|
|
||||||
Parsing::Literal => "a literal",
|
|
||||||
Parsing::Array => "an array",
|
Parsing::Array => "an array",
|
||||||
Parsing::ArrayRep => "an array of form [k;N]",
|
Parsing::ArrayRep => "an array of form [k;N]",
|
||||||
Parsing::AddrOf => "a borrow op",
|
Parsing::AddrOf => "a borrow op",
|
||||||
Parsing::Block => "a block",
|
Parsing::Block => "a block",
|
||||||
Parsing::Group => "a grouped expression",
|
Parsing::Group => "a grouped expression",
|
||||||
Parsing::Tuple => "a tuple",
|
Parsing::Tuple => "a tuple",
|
||||||
|
Parsing::Loop => "an unconditional loop expression",
|
||||||
Parsing::While => "a while expression",
|
Parsing::While => "a while expression",
|
||||||
Parsing::If => "an if expression",
|
Parsing::If => "an if expression",
|
||||||
Parsing::For => "a for expression",
|
Parsing::For => "a for expression",
|
||||||
@@ -203,6 +242,10 @@ impl Display for Parsing {
|
|||||||
Parsing::Break => "a break expression",
|
Parsing::Break => "a break expression",
|
||||||
Parsing::Return => "a return expression",
|
Parsing::Return => "a return expression",
|
||||||
Parsing::Continue => "a continue expression",
|
Parsing::Continue => "a continue expression",
|
||||||
|
|
||||||
|
Parsing::Pattern => "a pattern",
|
||||||
|
Parsing::Match => "a match expression",
|
||||||
|
Parsing::MatchArm => "a match arm",
|
||||||
}
|
}
|
||||||
.fmt(f)
|
.fmt(f)
|
||||||
}
|
}
|
||||||
118
compiler/cl-parser/src/inliner.rs
Normal file
118
compiler/cl-parser/src/inliner.rs
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
//! The [ModuleInliner] reads files described in the module structure of the
|
||||||
|
|
||||||
|
use crate::Parser;
|
||||||
|
use cl_ast::{ast_visitor::Fold, *};
|
||||||
|
use cl_lexer::Lexer;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
pub type IoErrs = Vec<(PathBuf, std::io::Error)>;
|
||||||
|
pub type ParseErrs = Vec<(PathBuf, crate::error::Error)>;
|
||||||
|
|
||||||
|
pub struct ModuleInliner {
|
||||||
|
path: PathBuf,
|
||||||
|
io_errs: IoErrs,
|
||||||
|
parse_errs: ParseErrs,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleInliner {
|
||||||
|
/// Creates a new [ModuleInliner]
|
||||||
|
pub fn new(root: impl AsRef<Path>) -> Self {
|
||||||
|
Self {
|
||||||
|
path: root.as_ref().to_path_buf(),
|
||||||
|
io_errs: Default::default(),
|
||||||
|
parse_errs: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true when the [ModuleInliner] has errors to report
|
||||||
|
pub fn has_errors(&self) -> bool {
|
||||||
|
!(self.io_errs.is_empty() && self.parse_errs.is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the [IO Errors](IoErrs) and [parse Errors](ParseErrs)
|
||||||
|
pub fn into_errs(self) -> Option<(IoErrs, ParseErrs)> {
|
||||||
|
self.has_errors().then_some((self.io_errs, self.parse_errs))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Traverses a [File], attempting to inline all submodules.
|
||||||
|
///
|
||||||
|
/// This is a simple wrapper around [ModuleInliner::fold_file()] and
|
||||||
|
/// [ModuleInliner::into_errs()]
|
||||||
|
pub fn inline(mut self, file: File) -> Result<File, (File, IoErrs, ParseErrs)> {
|
||||||
|
let file = self.fold_file(file);
|
||||||
|
|
||||||
|
match self.into_errs() {
|
||||||
|
Some((io, parse)) => Err((file, io, parse)),
|
||||||
|
None => Ok(file),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Records an [I/O error](std::io::Error) for later
|
||||||
|
fn handle_io_error(&mut self, error: std::io::Error) -> Option<File> {
|
||||||
|
self.io_errs.push((self.path.clone(), error));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Records a [parse error](crate::error::Error) for later
|
||||||
|
fn handle_parse_error(&mut self, error: crate::error::Error) -> Option<File> {
|
||||||
|
self.parse_errs.push((self.path.clone(), error));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fold for ModuleInliner {
|
||||||
|
/// Traverses down the module tree, entering ever nested directories
|
||||||
|
fn fold_module(&mut self, m: Module) -> Module {
|
||||||
|
let Module { name, file } = m;
|
||||||
|
self.path.push(&*name); // cd ./name
|
||||||
|
|
||||||
|
let file = self.fold_module_kind(file);
|
||||||
|
|
||||||
|
self.path.pop(); // cd ..
|
||||||
|
Module { name, file }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleInliner {
|
||||||
|
/// Attempts to read and parse a file for every module in the tree
|
||||||
|
fn fold_module_kind(&mut self, m: Option<File>) -> Option<File> {
|
||||||
|
use std::borrow::Cow;
|
||||||
|
if let Some(f) = m {
|
||||||
|
return Some(self.fold_file(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
// cd path/mod.cl
|
||||||
|
self.path.set_extension("cl");
|
||||||
|
let mut used_path: Cow<Path> = Cow::Borrowed(&self.path);
|
||||||
|
|
||||||
|
let file = match std::fs::read_to_string(&self.path) {
|
||||||
|
Err(error) => {
|
||||||
|
let Some(basename) = self.path.file_name() else {
|
||||||
|
return self.handle_io_error(error);
|
||||||
|
};
|
||||||
|
used_path = Cow::Owned(
|
||||||
|
self.path
|
||||||
|
.parent()
|
||||||
|
.and_then(Path::parent)
|
||||||
|
.map(|path| path.join(basename))
|
||||||
|
.unwrap_or_default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
match std::fs::read_to_string(&used_path) {
|
||||||
|
Err(error) => return self.handle_io_error(error),
|
||||||
|
Ok(file) => file,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(file) => file,
|
||||||
|
};
|
||||||
|
|
||||||
|
match Parser::new(used_path.display().to_string(), Lexer::new(&file)).parse() {
|
||||||
|
Err(e) => self.handle_parse_error(e),
|
||||||
|
Ok(file) => {
|
||||||
|
self.path.set_extension("");
|
||||||
|
// The newly loaded module may need further inlining
|
||||||
|
Some(self.fold_file(file))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,3 +14,5 @@ use cl_token::*;
|
|||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
|
||||||
|
pub mod inliner;
|
||||||
1255
compiler/cl-parser/src/parser.rs
Normal file
1255
compiler/cl-parser/src/parser.rs
Normal file
File diff suppressed because it is too large
Load Diff
425
compiler/cl-parser/src/parser/prec.rs
Normal file
425
compiler/cl-parser/src/parser/prec.rs
Normal file
@@ -0,0 +1,425 @@
|
|||||||
|
//! Parses an [ExprKind] using a modified pratt parser
|
||||||
|
//!
|
||||||
|
//! See also: [Expr::parse], [ExprKind::parse]
|
||||||
|
//!
|
||||||
|
//! Implementer's note: [ExprKind::parse] is the public API for parsing [ExprKind]s.
|
||||||
|
//! Do not call it from within this function.
|
||||||
|
|
||||||
|
use super::{Parse, *};
|
||||||
|
|
||||||
|
/// Parses an [ExprKind]
|
||||||
|
pub fn expr(p: &mut Parser, power: u8) -> PResult<Expr> {
|
||||||
|
let parsing = Parsing::ExprKind;
|
||||||
|
let start = p.loc();
|
||||||
|
// Prefix expressions
|
||||||
|
let mut head = Expr {
|
||||||
|
kind: match p.peek_kind(Parsing::Unary)? {
|
||||||
|
literal_like!() => Literal::parse(p)?.into(),
|
||||||
|
path_like!() => exprkind_pathlike(p)?,
|
||||||
|
TokenKind::Amp | TokenKind::AmpAmp => AddrOf::parse(p)?.into(),
|
||||||
|
TokenKind::Bar | TokenKind::BarBar => Closure::parse(p)?.into(),
|
||||||
|
TokenKind::Grave => Quote::parse(p)?.into(),
|
||||||
|
TokenKind::LCurly => Block::parse(p)?.into(),
|
||||||
|
TokenKind::LBrack => exprkind_arraylike(p)?,
|
||||||
|
TokenKind::LParen => exprkind_tuplelike(p)?,
|
||||||
|
TokenKind::Let => Let::parse(p)?.into(),
|
||||||
|
TokenKind::Match => Match::parse(p)?.into(),
|
||||||
|
TokenKind::While => ExprKind::While(While::parse(p)?),
|
||||||
|
TokenKind::If => ExprKind::If(If::parse(p)?),
|
||||||
|
TokenKind::For => ExprKind::For(For::parse(p)?),
|
||||||
|
TokenKind::Break => ExprKind::Break(Break::parse(p)?),
|
||||||
|
TokenKind::Return => ExprKind::Return(Return::parse(p)?),
|
||||||
|
TokenKind::Continue => {
|
||||||
|
p.consume_peeked();
|
||||||
|
ExprKind::Continue
|
||||||
|
}
|
||||||
|
|
||||||
|
op => {
|
||||||
|
let (kind, prec) =
|
||||||
|
from_prefix(op).ok_or_else(|| p.error(Unexpected(op), parsing))?;
|
||||||
|
let ((), after) = prec.prefix().expect("should have a precedence");
|
||||||
|
p.consume_peeked();
|
||||||
|
Unary { kind, tail: expr(p, after)?.into() }.into()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
span: Span(start, p.loc()),
|
||||||
|
};
|
||||||
|
|
||||||
|
fn from_postfix(op: TokenKind) -> Option<Precedence> {
|
||||||
|
Some(match op {
|
||||||
|
TokenKind::LBrack => Precedence::Index,
|
||||||
|
TokenKind::LParen => Precedence::Call,
|
||||||
|
TokenKind::LCurly => Precedence::Structor,
|
||||||
|
TokenKind::Dot => Precedence::Member,
|
||||||
|
TokenKind::As => Precedence::Cast,
|
||||||
|
_ => None?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Ok(op) = p.peek_kind(parsing) {
|
||||||
|
// Postfix expressions
|
||||||
|
if let Some((before, ())) = from_postfix(op).and_then(Precedence::postfix) {
|
||||||
|
if before < power {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
head = Expr {
|
||||||
|
kind: match op {
|
||||||
|
TokenKind::LBrack => {
|
||||||
|
p.consume_peeked();
|
||||||
|
let indices =
|
||||||
|
sep(Expr::parse, TokenKind::Comma, TokenKind::RBrack, parsing)(p)?;
|
||||||
|
p.match_type(TokenKind::RBrack, parsing)?;
|
||||||
|
ExprKind::Index(Index { head: head.into(), indices })
|
||||||
|
}
|
||||||
|
TokenKind::LParen => {
|
||||||
|
p.consume_peeked();
|
||||||
|
let exprs =
|
||||||
|
sep(Expr::parse, TokenKind::Comma, TokenKind::RParen, parsing)(p)?;
|
||||||
|
p.match_type(TokenKind::RParen, parsing)?;
|
||||||
|
Binary {
|
||||||
|
kind: BinaryKind::Call,
|
||||||
|
parts: (
|
||||||
|
head,
|
||||||
|
Expr { kind: Tuple { exprs }.into(), span: Span(start, p.loc()) },
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
TokenKind::LCurly => match head.kind {
|
||||||
|
ExprKind::Path(path) => ExprKind::Structor(structor_body(p, path)?),
|
||||||
|
_ => break,
|
||||||
|
},
|
||||||
|
TokenKind::Dot => {
|
||||||
|
p.consume_peeked();
|
||||||
|
let kind = MemberKind::parse(p)?;
|
||||||
|
Member { head: Box::new(head), kind }.into()
|
||||||
|
}
|
||||||
|
TokenKind::As => {
|
||||||
|
p.consume_peeked();
|
||||||
|
let ty = Ty::parse(p)?;
|
||||||
|
Cast { head: head.into(), ty }.into()
|
||||||
|
}
|
||||||
|
_ => Err(p.error(Unexpected(op), parsing))?,
|
||||||
|
},
|
||||||
|
span: Span(start, p.loc()),
|
||||||
|
};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// infix expressions
|
||||||
|
if let Some((kind, prec)) = from_infix(op) {
|
||||||
|
let (before, after) = prec.infix().expect("should have a precedence");
|
||||||
|
if before < power {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p.consume_peeked();
|
||||||
|
|
||||||
|
let tail = expr(p, after)?;
|
||||||
|
head = Expr {
|
||||||
|
kind: Binary { kind, parts: (head, tail).into() }.into(),
|
||||||
|
span: Span(start, p.loc()),
|
||||||
|
};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((kind, prec)) = from_modify(op) {
|
||||||
|
let (before, after) = prec.infix().expect("should have a precedence");
|
||||||
|
if before < power {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p.consume_peeked();
|
||||||
|
|
||||||
|
let tail = expr(p, after)?;
|
||||||
|
head = Expr {
|
||||||
|
kind: Modify { kind, parts: (head, tail).into() }.into(),
|
||||||
|
span: Span(start, p.loc()),
|
||||||
|
};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let TokenKind::Eq = op {
|
||||||
|
let (before, after) = Precedence::Assign
|
||||||
|
.infix()
|
||||||
|
.expect("should have a precedence");
|
||||||
|
if before < power {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p.consume_peeked();
|
||||||
|
|
||||||
|
let tail = expr(p, after)?;
|
||||||
|
head = Expr {
|
||||||
|
kind: Assign { parts: (head, tail).into() }.into(),
|
||||||
|
span: Span(start, p.loc()),
|
||||||
|
};
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let TokenKind::As = op {
|
||||||
|
let before = Precedence::Cast.level();
|
||||||
|
if before < power {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p.consume_peeked();
|
||||||
|
|
||||||
|
let ty = Ty::parse(p)?;
|
||||||
|
head = Expr { kind: Cast { head: head.into(), ty }.into(), span: Span(start, p.loc()) };
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(head)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [Array] = '[' ([Expr] ',')* [Expr]? ']'
|
||||||
|
///
|
||||||
|
/// Array and ArrayRef are ambiguous until the second token,
|
||||||
|
/// so they can't be independent subexpressions
|
||||||
|
fn exprkind_arraylike(p: &mut Parser) -> PResult<ExprKind> {
|
||||||
|
const P: Parsing = Parsing::Array;
|
||||||
|
const START: TokenKind = TokenKind::LBrack;
|
||||||
|
const END: TokenKind = TokenKind::RBrack;
|
||||||
|
|
||||||
|
p.match_type(START, P)?;
|
||||||
|
let out = match p.peek_kind(P)? {
|
||||||
|
END => Array { values: vec![] }.into(),
|
||||||
|
_ => exprkind_array_rep(p)?,
|
||||||
|
};
|
||||||
|
p.match_type(END, P)?;
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [ArrayRep] = `[` [Expr] `;` [Expr] `]`
|
||||||
|
fn exprkind_array_rep(p: &mut Parser) -> PResult<ExprKind> {
|
||||||
|
const P: Parsing = Parsing::Array;
|
||||||
|
const END: TokenKind = TokenKind::RBrack;
|
||||||
|
|
||||||
|
let first = Expr::parse(p)?;
|
||||||
|
Ok(match p.peek_kind(P)? {
|
||||||
|
TokenKind::Semi => ArrayRep {
|
||||||
|
value: first.into(),
|
||||||
|
repeat: {
|
||||||
|
p.consume_peeked();
|
||||||
|
p.parse()?
|
||||||
|
},
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
TokenKind::RBrack => Array { values: vec![first] }.into(),
|
||||||
|
TokenKind::Comma => Array {
|
||||||
|
values: {
|
||||||
|
p.consume_peeked();
|
||||||
|
let mut out = vec![first];
|
||||||
|
out.extend(sep(Expr::parse, TokenKind::Comma, END, P)(p)?);
|
||||||
|
out
|
||||||
|
},
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
ty => Err(p.error(Unexpected(ty), P))?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [Group] = `(`([Empty](ExprKind::Empty)|[Expr]|[Tuple])`)`
|
||||||
|
///
|
||||||
|
/// [ExprKind::Empty] and [Group] are special cases of [Tuple]
|
||||||
|
fn exprkind_tuplelike(p: &mut Parser) -> PResult<ExprKind> {
|
||||||
|
p.match_type(TokenKind::LParen, Parsing::Group)?;
|
||||||
|
let out = match p.peek_kind(Parsing::Group)? {
|
||||||
|
TokenKind::RParen => Ok(ExprKind::Empty),
|
||||||
|
_ => exprkind_group(p),
|
||||||
|
};
|
||||||
|
p.match_type(TokenKind::RParen, Parsing::Group)?;
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [Group] = `(`([Empty](ExprKind::Empty)|[Expr]|[Tuple])`)`
|
||||||
|
fn exprkind_group(p: &mut Parser) -> PResult<ExprKind> {
|
||||||
|
let first = Expr::parse(p)?;
|
||||||
|
match p.peek_kind(Parsing::Group)? {
|
||||||
|
TokenKind::Comma => {
|
||||||
|
let mut exprs = vec![first];
|
||||||
|
p.consume_peeked();
|
||||||
|
while TokenKind::RParen != p.peek_kind(Parsing::Tuple)? {
|
||||||
|
exprs.push(Expr::parse(p)?);
|
||||||
|
match p.peek_kind(Parsing::Tuple)? {
|
||||||
|
TokenKind::Comma => p.consume_peeked(),
|
||||||
|
_ => break,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(Tuple { exprs }.into())
|
||||||
|
}
|
||||||
|
_ => Ok(Group { expr: first.into() }.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses an expression beginning with a [Path] (i.e. [Path] or [Structor])
|
||||||
|
fn exprkind_pathlike(p: &mut Parser) -> PResult<ExprKind> {
|
||||||
|
Path::parse(p).map(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [Structor]Body = `{` ([Fielder] `,`)* [Fielder]? `}`
|
||||||
|
fn structor_body(p: &mut Parser, to: Path) -> PResult<Structor> {
|
||||||
|
let init = delim(
|
||||||
|
sep(
|
||||||
|
Fielder::parse,
|
||||||
|
TokenKind::Comma,
|
||||||
|
CURLIES.1,
|
||||||
|
Parsing::Structor,
|
||||||
|
),
|
||||||
|
CURLIES,
|
||||||
|
Parsing::Structor,
|
||||||
|
)(p)?;
|
||||||
|
|
||||||
|
Ok(Structor { to, init })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Precedence provides a total ordering among operators
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Precedence {
|
||||||
|
Assign,
|
||||||
|
Structor, // A structor is never a valid conditional
|
||||||
|
Condition, // Anything that syntactically needs a block following it
|
||||||
|
Logic,
|
||||||
|
Compare,
|
||||||
|
Range,
|
||||||
|
Bitwise,
|
||||||
|
Shift,
|
||||||
|
Factor,
|
||||||
|
Term,
|
||||||
|
Unary,
|
||||||
|
Index,
|
||||||
|
Cast,
|
||||||
|
Member, // left-associative
|
||||||
|
Call,
|
||||||
|
Deref,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Precedence {
|
||||||
|
#[inline]
|
||||||
|
pub const fn level(self) -> u8 {
|
||||||
|
(self as u8) << 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prefix(self) -> Option<((), u8)> {
|
||||||
|
match self {
|
||||||
|
Self::Assign => Some(((), self.level())),
|
||||||
|
Self::Unary => Some(((), self.level())),
|
||||||
|
Self::Deref => Some(((), self.level())),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn infix(self) -> Option<(u8, u8)> {
|
||||||
|
let level = self.level();
|
||||||
|
match self {
|
||||||
|
Self::Unary => None,
|
||||||
|
Self::Assign => Some((level + 1, level)),
|
||||||
|
_ => Some((level, level + 1)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn postfix(self) -> Option<(u8, ())> {
|
||||||
|
match self {
|
||||||
|
Self::Structor | Self::Index | Self::Call | Self::Member | Self::Cast => {
|
||||||
|
Some((self.level(), ()))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ModifyKind> for Precedence {
|
||||||
|
fn from(_value: ModifyKind) -> Self {
|
||||||
|
Precedence::Assign
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BinaryKind> for Precedence {
|
||||||
|
fn from(value: BinaryKind) -> Self {
|
||||||
|
use BinaryKind as Op;
|
||||||
|
match value {
|
||||||
|
Op::Call => Precedence::Call,
|
||||||
|
Op::Mul | Op::Div | Op::Rem => Precedence::Term,
|
||||||
|
Op::Add | Op::Sub => Precedence::Factor,
|
||||||
|
Op::Shl | Op::Shr => Precedence::Shift,
|
||||||
|
Op::BitAnd | Op::BitOr | Op::BitXor => Precedence::Bitwise,
|
||||||
|
Op::LogAnd | Op::LogOr | Op::LogXor => Precedence::Logic,
|
||||||
|
Op::RangeExc | Op::RangeInc => Precedence::Range,
|
||||||
|
Op::Lt | Op::LtEq | Op::Equal | Op::NotEq | Op::GtEq | Op::Gt => Precedence::Compare,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UnaryKind> for Precedence {
|
||||||
|
fn from(value: UnaryKind) -> Self {
|
||||||
|
use UnaryKind as Op;
|
||||||
|
match value {
|
||||||
|
Op::Loop => Precedence::Assign,
|
||||||
|
Op::Deref => Precedence::Deref,
|
||||||
|
_ => Precedence::Unary,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates helper functions for turning TokenKinds into AST operators
|
||||||
|
macro operator($($name:ident ($takes:ident => $returns:ident) {$($t:ident => $p:ident),*$(,)?};)*) {$(
|
||||||
|
pub fn $name (value: $takes) -> Option<($returns, Precedence)> {
|
||||||
|
match value {
|
||||||
|
$($takes::$t => Some(($returns::$p, Precedence::from($returns::$p))),)*
|
||||||
|
_ => None?,
|
||||||
|
}
|
||||||
|
})*
|
||||||
|
}
|
||||||
|
|
||||||
|
operator! {
|
||||||
|
from_prefix (TokenKind => UnaryKind) {
|
||||||
|
Loop => Loop,
|
||||||
|
Star => Deref,
|
||||||
|
Minus => Neg,
|
||||||
|
Bang => Not,
|
||||||
|
DotDot => RangeExc,
|
||||||
|
DotDotEq => RangeInc,
|
||||||
|
At => At,
|
||||||
|
Tilde => Tilde,
|
||||||
|
};
|
||||||
|
|
||||||
|
from_modify(TokenKind => ModifyKind) {
|
||||||
|
AmpEq => And,
|
||||||
|
BarEq => Or,
|
||||||
|
XorEq => Xor,
|
||||||
|
LtLtEq => Shl,
|
||||||
|
GtGtEq => Shr,
|
||||||
|
PlusEq => Add,
|
||||||
|
MinusEq => Sub,
|
||||||
|
StarEq => Mul,
|
||||||
|
SlashEq => Div,
|
||||||
|
RemEq => Rem,
|
||||||
|
};
|
||||||
|
|
||||||
|
from_infix (TokenKind => BinaryKind) {
|
||||||
|
Lt => Lt,
|
||||||
|
LtEq => LtEq,
|
||||||
|
EqEq => Equal,
|
||||||
|
BangEq => NotEq,
|
||||||
|
GtEq => GtEq,
|
||||||
|
Gt => Gt,
|
||||||
|
DotDot => RangeExc,
|
||||||
|
DotDotEq => RangeInc,
|
||||||
|
AmpAmp => LogAnd,
|
||||||
|
BarBar => LogOr,
|
||||||
|
XorXor => LogXor,
|
||||||
|
Amp => BitAnd,
|
||||||
|
Bar => BitOr,
|
||||||
|
Xor => BitXor,
|
||||||
|
LtLt => Shl,
|
||||||
|
GtGt => Shr,
|
||||||
|
Plus => Add,
|
||||||
|
Minus => Sub,
|
||||||
|
Star => Mul,
|
||||||
|
Slash => Div,
|
||||||
|
Rem => Rem,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -14,10 +14,9 @@ cl-ast = { path = "../cl-ast" }
|
|||||||
cl-lexer = { path = "../cl-lexer" }
|
cl-lexer = { path = "../cl-lexer" }
|
||||||
cl-token = { path = "../cl-token" }
|
cl-token = { path = "../cl-token" }
|
||||||
cl-parser = { path = "../cl-parser" }
|
cl-parser = { path = "../cl-parser" }
|
||||||
cl-interpret = { path = "../cl-interpret" }
|
|
||||||
crossterm = "0.27.0"
|
|
||||||
argh = "0.1.12"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
cl-structures = { path = "../cl-structures" }
|
|
||||||
cl-typeck = { path = "../cl-typeck" }
|
cl-typeck = { path = "../cl-typeck" }
|
||||||
|
cl-interpret = { path = "../cl-interpret" }
|
||||||
|
cl-structures = { path = "../cl-structures" }
|
||||||
|
cl-arena = { version = "0", registry = "soft-fish" }
|
||||||
|
repline = { version = "*", registry = "soft-fish" }
|
||||||
|
argwerk = "0.20.4"
|
||||||
@@ -4,7 +4,7 @@ use cl_lexer::Lexer;
|
|||||||
use cl_token::Token;
|
use cl_token::Token;
|
||||||
use std::{
|
use std::{
|
||||||
error::Error,
|
error::Error,
|
||||||
io::{stdin, IsTerminal, Read},
|
io::{IsTerminal, Read, stdin},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
951
compiler/cl-repl/examples/to_c.rs
Normal file
951
compiler/cl-repl/examples/to_c.rs
Normal file
@@ -0,0 +1,951 @@
|
|||||||
|
//! Pretty prints a conlang AST in yaml
|
||||||
|
|
||||||
|
use cl_ast::{File, Stmt};
|
||||||
|
use cl_lexer::Lexer;
|
||||||
|
use cl_parser::Parser;
|
||||||
|
use repline::{Repline, error::Error as RlError};
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
if let Some(path) = std::env::args().nth(1) {
|
||||||
|
let f = std::fs::read_to_string(&path).expect("Path must be valid.");
|
||||||
|
let mut parser = Parser::new(path, Lexer::new(&f));
|
||||||
|
let code: File = match parser.parse() {
|
||||||
|
Ok(f) => f,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{e}");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
CLangifier::new().p(&code);
|
||||||
|
println!();
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let mut rl = Repline::new("\x1b[33m", "cl>", "? >");
|
||||||
|
loop {
|
||||||
|
let mut line = match rl.read() {
|
||||||
|
Err(RlError::CtrlC(_)) => break,
|
||||||
|
Err(RlError::CtrlD(line)) => {
|
||||||
|
rl.deny();
|
||||||
|
line
|
||||||
|
}
|
||||||
|
Ok(line) => line,
|
||||||
|
Err(e) => Err(e)?,
|
||||||
|
};
|
||||||
|
if !line.ends_with(';') {
|
||||||
|
line.push(';');
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut parser = Parser::new("stdin", Lexer::new(&line));
|
||||||
|
let code = match parser.parse::<Stmt>() {
|
||||||
|
Ok(code) => {
|
||||||
|
rl.accept();
|
||||||
|
code
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
print!("\x1b[40G\x1bJ\x1b[91m{e}\x1b[0m");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
print!("\x1b[G\x1b[J");
|
||||||
|
CLangifier::new().p(&code);
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use clangifier::CLangifier;
|
||||||
|
pub mod clangifier {
|
||||||
|
use crate::clangify::CLangify;
|
||||||
|
use std::{
|
||||||
|
fmt::Display,
|
||||||
|
io::Write,
|
||||||
|
ops::{Add, Deref, DerefMut},
|
||||||
|
};
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct CLangifier {
|
||||||
|
depth: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CLangifier {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn indent(&mut self) -> Section<'_> {
|
||||||
|
Section::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prints a [Yamlify] value
|
||||||
|
#[inline]
|
||||||
|
pub fn p<T: CLangify + ?Sized>(&mut self, yaml: &T) -> &mut Self {
|
||||||
|
yaml.print(self);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn increase(&mut self) {
|
||||||
|
self.depth += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decrease(&mut self) {
|
||||||
|
self.depth -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_indentation(&self, writer: &mut impl Write) {
|
||||||
|
for _ in 0..self.depth {
|
||||||
|
let _ = write!(writer, " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn endl(&mut self) -> &mut Self {
|
||||||
|
self.p("\n")
|
||||||
|
.print_indentation(&mut std::io::stdout().lock());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prints a section header and increases indentation
|
||||||
|
pub fn nest(&mut self, name: impl Display) -> Section<'_> {
|
||||||
|
print!("{name}");
|
||||||
|
self.indent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: CLangify + ?Sized> Add<&C> for &mut CLangifier {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: &C) -> Self::Output {
|
||||||
|
self.p(rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tracks the start and end of an indented block (a "section")
|
||||||
|
pub struct Section<'y> {
|
||||||
|
yamler: &'y mut CLangifier,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'y> Section<'y> {
|
||||||
|
pub fn new(yamler: &'y mut CLangifier) -> Self {
|
||||||
|
yamler.increase();
|
||||||
|
Self { yamler }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Section<'_> {
|
||||||
|
type Target = CLangifier;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.yamler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl DerefMut for Section<'_> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
self.yamler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Section<'_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let Self { yamler } = self;
|
||||||
|
yamler.decrease();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod clangify {
|
||||||
|
use core::panic;
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
|
use super::clangifier::CLangifier;
|
||||||
|
use cl_ast::*;
|
||||||
|
|
||||||
|
pub trait CLangify {
|
||||||
|
fn print(&self, y: &mut CLangifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CLangify for File {
|
||||||
|
fn print(&self, mut y: &mut CLangifier) {
|
||||||
|
let File { name, items } = self;
|
||||||
|
// TODO: turn name into include guard
|
||||||
|
y = (y + "// Generated from " + name).endl();
|
||||||
|
for (idx, item) in items.iter().enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
y.endl().endl();
|
||||||
|
}
|
||||||
|
y.p(item);
|
||||||
|
}
|
||||||
|
y.endl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Visibility {
|
||||||
|
fn print(&self, _y: &mut CLangifier) {}
|
||||||
|
}
|
||||||
|
impl CLangify for Mutability {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
if let Mutability::Not = self {
|
||||||
|
y.p("const ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CLangify for Attrs {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { meta } = self;
|
||||||
|
y.nest("Attrs").p(meta);
|
||||||
|
todo!("Attributes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Meta {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { name, kind } = self;
|
||||||
|
y.nest("Meta").p(name).p(kind);
|
||||||
|
todo!("Attributes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for MetaKind {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
match self {
|
||||||
|
MetaKind::Plain => y,
|
||||||
|
MetaKind::Equals(value) => y.p(value),
|
||||||
|
MetaKind::Func(args) => y.p(args),
|
||||||
|
};
|
||||||
|
todo!("Attributes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CLangify for Item {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { span: _, attrs: _, vis, kind } = self;
|
||||||
|
y.p(vis).p(kind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for ItemKind {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
match self {
|
||||||
|
ItemKind::Alias(f) => y.p(f),
|
||||||
|
ItemKind::Const(f) => y.p(f),
|
||||||
|
ItemKind::Static(f) => y.p(f),
|
||||||
|
ItemKind::Module(f) => y.p(f),
|
||||||
|
ItemKind::Function(f) => y.p(f),
|
||||||
|
ItemKind::Struct(f) => y.p(f),
|
||||||
|
ItemKind::Enum(f) => y.p(f),
|
||||||
|
ItemKind::Impl(f) => y.p(f),
|
||||||
|
ItemKind::Use(f) => y.p(f),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Generics {
|
||||||
|
fn print(&self, _y: &mut CLangifier) {
|
||||||
|
let Self { vars } = self;
|
||||||
|
if !vars.is_empty() {
|
||||||
|
panic!("C doesn't have generics, dumbass.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Alias {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { name, from } = self;
|
||||||
|
y.p("typedef ").p(from).p(" ");
|
||||||
|
y.p(name).p("; ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Const {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { name, ty, init } = self;
|
||||||
|
y.p("const ").p(ty).p(" ");
|
||||||
|
y.p(name).p(" = ").p(init);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Static {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { mutable, name, ty, init } = self;
|
||||||
|
y.p(mutable).p(ty).p(" ");
|
||||||
|
y.p(name).p(" = ").p(init);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Module {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { name, file } = self;
|
||||||
|
y.nest("// mod ").p(name).p(" {").endl();
|
||||||
|
y.p(file);
|
||||||
|
y.endl().p("// } mod ").p(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Function {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { name, gens: _, sign, bind, body } = self;
|
||||||
|
let TyFn { args, rety } = sign;
|
||||||
|
let types = match &args.kind {
|
||||||
|
TyKind::Tuple(TyTuple { types }) => types.as_slice(),
|
||||||
|
_ => panic!("Unsupported function args: {args}"),
|
||||||
|
};
|
||||||
|
let bind = match bind {
|
||||||
|
Pattern::Tuple(tup) => tup.as_slice(),
|
||||||
|
_ => panic!("Unsupported function binders: {args}"),
|
||||||
|
};
|
||||||
|
y.p(rety).p(" ").p(name).p(" (");
|
||||||
|
for (idx, (bind, ty)) in bind.iter().zip(types).enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
y.p(", ");
|
||||||
|
}
|
||||||
|
// y.print("/* TODO: desugar pat match args */");
|
||||||
|
y.p(ty).p(" ").p(bind);
|
||||||
|
}
|
||||||
|
y.p(") ").p(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Struct {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { name, gens: _, kind } = self;
|
||||||
|
y.p("struct ").p(name).nest(" {").p(kind);
|
||||||
|
y.endl().p("}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for StructKind {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
match self {
|
||||||
|
StructKind::Empty => y.endl().p("char _zero_sized_t;"),
|
||||||
|
StructKind::Tuple(k) => {
|
||||||
|
for (idx, ty) in k.iter().enumerate() {
|
||||||
|
y.endl().p(ty).p(" _").p(&idx).p(";");
|
||||||
|
}
|
||||||
|
y
|
||||||
|
}
|
||||||
|
StructKind::Struct(k) => y.p(k),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for StructMember {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { vis, name, ty } = self;
|
||||||
|
y.p(vis).p(ty).p(" ").p(name).p(";");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Enum {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { name, gens: _, variants } = self;
|
||||||
|
y.nest("enum ").p(name).p(" {").endl();
|
||||||
|
for (idx, variant) in variants.iter().enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
y.p(",").endl();
|
||||||
|
}
|
||||||
|
y.p(variant);
|
||||||
|
}
|
||||||
|
y.endl().p("\n}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Variant {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { name, kind, body } = self;
|
||||||
|
y.p(name).p(kind).p(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Impl {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { gens, target, body } = self;
|
||||||
|
y.nest("/* TODO: impl ").p(gens).p(target).p(" { */ ");
|
||||||
|
y.p(body);
|
||||||
|
y.p("/* } // impl ").p(target).p(" */ ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for ImplKind {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
match self {
|
||||||
|
ImplKind::Type(t) => y.p(t),
|
||||||
|
ImplKind::Trait { impl_trait, for_type } => {
|
||||||
|
todo!("impl {impl_trait} for {for_type}")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Use {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { absolute: _, tree } = self;
|
||||||
|
y.p(tree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for UseTree {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
match self {
|
||||||
|
UseTree::Tree(trees) => y.p(trees),
|
||||||
|
UseTree::Path(path, tree) => y.p("/* ").p(path).p(" */").p(tree),
|
||||||
|
UseTree::Alias(from, to) => y.p("#import <").p(from).p(">.h// ").p(to).p(" "),
|
||||||
|
UseTree::Name(name) => y.p("#import <").p(name).p(".h> "),
|
||||||
|
UseTree::Glob => y.p("/* TODO: use globbing */"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Block {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { stmts } = self;
|
||||||
|
{
|
||||||
|
let mut y = y.nest("{");
|
||||||
|
y.endl();
|
||||||
|
if let [
|
||||||
|
stmts @ ..,
|
||||||
|
Stmt { span: _, kind: StmtKind::Expr(expr), semi: Semi::Unterminated },
|
||||||
|
] = stmts.as_slice()
|
||||||
|
{
|
||||||
|
y.p(stmts).p("return ").p(expr).p(";");
|
||||||
|
} else {
|
||||||
|
y.p(stmts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
y.endl().p("}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Stmt {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { span: _, kind, semi: _ } = self;
|
||||||
|
y.p(kind).p(";").endl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Semi {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
y.p(";");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for StmtKind {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
match self {
|
||||||
|
StmtKind::Empty => y,
|
||||||
|
StmtKind::Item(s) => y.p(s),
|
||||||
|
StmtKind::Expr(s) => y.p(s),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Expr {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { span: _, kind } = self;
|
||||||
|
y.p(kind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for ExprKind {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
match self {
|
||||||
|
ExprKind::Closure(k) => todo!("Downgrade {k}"),
|
||||||
|
ExprKind::Quote(k) => k.print(y),
|
||||||
|
ExprKind::Let(k) => k.print(y),
|
||||||
|
ExprKind::Match(k) => k.print(y),
|
||||||
|
ExprKind::Assign(k) => k.print(y),
|
||||||
|
ExprKind::Modify(k) => k.print(y),
|
||||||
|
ExprKind::Binary(k) => k.print(y),
|
||||||
|
ExprKind::Unary(k) => k.print(y),
|
||||||
|
ExprKind::Cast(k) => k.print(y),
|
||||||
|
ExprKind::Member(k) => k.print(y),
|
||||||
|
ExprKind::Index(k) => k.print(y),
|
||||||
|
ExprKind::Structor(k) => k.print(y),
|
||||||
|
ExprKind::Path(k) => k.print(y),
|
||||||
|
ExprKind::Literal(k) => k.print(y),
|
||||||
|
ExprKind::Array(k) => k.print(y),
|
||||||
|
ExprKind::ArrayRep(k) => k.print(y),
|
||||||
|
ExprKind::AddrOf(k) => k.print(y),
|
||||||
|
ExprKind::Block(k) => k.print(y),
|
||||||
|
ExprKind::Empty => {}
|
||||||
|
ExprKind::Group(k) => k.print(y),
|
||||||
|
ExprKind::Tuple(k) => k.print(y),
|
||||||
|
ExprKind::While(k) => k.print(y),
|
||||||
|
ExprKind::If(k) => k.print(y),
|
||||||
|
ExprKind::For(k) => k.print(y),
|
||||||
|
ExprKind::Break(k) => k.print(y),
|
||||||
|
ExprKind::Return(k) => k.print(y),
|
||||||
|
ExprKind::Continue => {
|
||||||
|
y.nest("continue");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Quote {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
y.nest("\"");
|
||||||
|
print!("{self}");
|
||||||
|
y.p("\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Let {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { mutable, name, ty, init } = self;
|
||||||
|
let ty = ty.as_deref().map(|ty| &ty.kind).unwrap_or(&TyKind::Infer);
|
||||||
|
match ty {
|
||||||
|
TyKind::Array(TyArray { ty, count }) => {
|
||||||
|
y.p(ty).p(" ").p(mutable).p(name).p("[").p(count).p("]");
|
||||||
|
}
|
||||||
|
TyKind::Fn(TyFn { args, rety }) => {
|
||||||
|
y.nest("(").p(rety).p(" *").p(mutable).p(name).p(")(");
|
||||||
|
match &args.kind {
|
||||||
|
TyKind::Tuple(TyTuple { types }) => {
|
||||||
|
for (idx, ty) in types.iter().enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
y.p(", ");
|
||||||
|
}
|
||||||
|
y.p(ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
y.p(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
y.p(")");
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
y.indent().p(ty).p(" ").p(mutable).p(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(init) = init {
|
||||||
|
y.p(" = ").p(init);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CLangify for Pattern {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
// TODO: Pattern match desugaring!!!
|
||||||
|
match self {
|
||||||
|
Pattern::Name(name) => y.p(name),
|
||||||
|
Pattern::Path(path) => y.p(path),
|
||||||
|
Pattern::Literal(literal) => y.p(literal),
|
||||||
|
Pattern::Rest(name) => y.p("..").p(name),
|
||||||
|
Pattern::Ref(mutability, pattern) => y.p("&").p(mutability).p(pattern),
|
||||||
|
Pattern::RangeExc(head, tail) => y.p("RangeExc").p(head).p(tail),
|
||||||
|
Pattern::RangeInc(head, tail) => y.p("RangeExc").p(head).p(tail),
|
||||||
|
Pattern::Tuple(patterns) => y.nest("Tuple").p(patterns),
|
||||||
|
Pattern::Array(patterns) => y.nest("Array").p(patterns),
|
||||||
|
Pattern::Struct(path, items) => {
|
||||||
|
{
|
||||||
|
let mut y = y.nest("Struct");
|
||||||
|
y.p(path);
|
||||||
|
for (name, item) in items {
|
||||||
|
y.p(name).p(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
y
|
||||||
|
}
|
||||||
|
Pattern::TupleStruct(path, items) => {
|
||||||
|
{
|
||||||
|
let mut y = y.nest("TupleStruct");
|
||||||
|
y.p(path).p(items);
|
||||||
|
}
|
||||||
|
y
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Match {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { scrutinee, arms } = self;
|
||||||
|
y.p("/* match ").p(scrutinee);
|
||||||
|
y.nest(" { ").p(arms);
|
||||||
|
y.p(" } */");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CLangify for MatchArm {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self(pat, expr) = self;
|
||||||
|
y.p(pat).p(" => ").p(expr).p(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Assign {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { parts } = self;
|
||||||
|
y.p(&parts.0).p(" = ").p(&parts.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Modify {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { kind, parts } = self;
|
||||||
|
y.p(&parts.0).p(kind).p(&parts.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for ModifyKind {
|
||||||
|
fn print(&self, _y: &mut CLangifier) {
|
||||||
|
print!(" {self} ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Binary {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { kind, parts } = self;
|
||||||
|
match kind {
|
||||||
|
BinaryKind::Call => y.p(&parts.0).p(&parts.1),
|
||||||
|
_ => y.p("(").p(&parts.0).p(kind).p(&parts.1).p(")"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for BinaryKind {
|
||||||
|
fn print(&self, _y: &mut CLangifier) {
|
||||||
|
print!(" {self} ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Unary {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { kind, tail } = self;
|
||||||
|
match kind {
|
||||||
|
UnaryKind::Deref => y.p("*").p(tail),
|
||||||
|
UnaryKind::Neg => y.p("-").p(tail),
|
||||||
|
UnaryKind::Not => y.p("!").p(tail),
|
||||||
|
UnaryKind::RangeInc => todo!("Unary RangeInc in C"),
|
||||||
|
UnaryKind::RangeExc => todo!("Unary RangeExc in C"),
|
||||||
|
UnaryKind::Loop => y.nest("while (1) { ").p(tail).p(" }"),
|
||||||
|
UnaryKind::At => todo!(),
|
||||||
|
UnaryKind::Tilde => todo!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Cast {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { head, ty } = self;
|
||||||
|
y.nest("(").p(ty).p(")");
|
||||||
|
y.p(head);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Member {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { head, kind } = self;
|
||||||
|
match kind {
|
||||||
|
MemberKind::Call(name, Tuple { exprs }) => {
|
||||||
|
y.p(name);
|
||||||
|
y.p("(");
|
||||||
|
for (idx, expr) in iter::once(head.as_ref()).chain(exprs).enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
y.p(", ");
|
||||||
|
}
|
||||||
|
y.p(expr);
|
||||||
|
}
|
||||||
|
y.p(")")
|
||||||
|
}
|
||||||
|
MemberKind::Struct(name) => y.p(head).p(".").p(name),
|
||||||
|
MemberKind::Tuple(idx) => y.p(head).p("._").p(idx),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Tuple {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { exprs } = self;
|
||||||
|
let mut y = y.nest("( ");
|
||||||
|
for (idx, expr) in exprs.iter().enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
y.p(", ");
|
||||||
|
}
|
||||||
|
y.p(expr);
|
||||||
|
}
|
||||||
|
y.p(" )");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Index {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { head, indices } = self;
|
||||||
|
y.p(head);
|
||||||
|
for index in indices {
|
||||||
|
y.p("[").p(index).p("]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Structor {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { to, init } = self;
|
||||||
|
y.nest("(").p(to).p(")");
|
||||||
|
{
|
||||||
|
let mut y = y.nest("{ ");
|
||||||
|
for (idx, field) in init.iter().enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
y.p(", ");
|
||||||
|
}
|
||||||
|
y.p(field);
|
||||||
|
}
|
||||||
|
y.p(init);
|
||||||
|
}
|
||||||
|
y.p("}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Fielder {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { name, init } = self;
|
||||||
|
y.p(".").p(name).p(" = ").p(init);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Array {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { values } = self;
|
||||||
|
{
|
||||||
|
let mut y = y.nest("{");
|
||||||
|
y.endl();
|
||||||
|
for (idx, value) in values.iter().enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
y.p(", ");
|
||||||
|
}
|
||||||
|
y.p(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
y.endl().p("}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for ArrayRep {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { value, repeat } = self;
|
||||||
|
let ExprKind::Literal(Literal::Int(repeat)) = &repeat.kind else {
|
||||||
|
eprintln!("Constant needs folding: {repeat}");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
{
|
||||||
|
let mut y = y.nest("{");
|
||||||
|
for _ in 0..*repeat {
|
||||||
|
y.endl().p(value).p(",");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
y.endl().p("}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for AddrOf {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { mutable: _, expr } = self;
|
||||||
|
y.p("&").p(expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Group {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { expr } = self;
|
||||||
|
y.p("(").p(expr).p(")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for While {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
// TODO: to properly propagate intermediate values, a new temp variable needs to be
|
||||||
|
// declared on every line lmao. This will require type info.
|
||||||
|
let Self { cond, pass, fail } = self;
|
||||||
|
let Else { body: fail } = fail;
|
||||||
|
y.nest("while(1) {")
|
||||||
|
.endl()
|
||||||
|
.p("if (")
|
||||||
|
.p(cond)
|
||||||
|
.p(") ")
|
||||||
|
.p(pass);
|
||||||
|
{
|
||||||
|
let mut y = y.nest(" else {");
|
||||||
|
y.endl();
|
||||||
|
if let Some(fail) = fail {
|
||||||
|
y.p(fail).p(";").endl();
|
||||||
|
}
|
||||||
|
y.p("break;");
|
||||||
|
}
|
||||||
|
y.endl().p("}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Else {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { body } = self;
|
||||||
|
if let Some(body) = body {
|
||||||
|
y.p(" else ").p(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for If {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { cond, pass, fail } = self;
|
||||||
|
y.p("if (").p(cond).p(")");
|
||||||
|
y.p(pass).p(fail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for For {
|
||||||
|
#[rustfmt::skip]
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { bind, cond, pass, fail: _ } = self;
|
||||||
|
let (mode, (head, tail)) = match &cond.kind {
|
||||||
|
ExprKind::Binary(Binary { kind: BinaryKind::RangeExc, parts }) => (false, &**parts),
|
||||||
|
ExprKind::Binary(Binary { kind: BinaryKind::RangeInc, parts }) => (true, &**parts),
|
||||||
|
_ => todo!("Clangify for loops"),
|
||||||
|
};
|
||||||
|
// for (int bind = head; bind mode? < : <= tail; bind++);
|
||||||
|
y.p("for ( int ").p(bind).p(" = ").p(head).p("; ");
|
||||||
|
y.p(bind).p(if mode {"<="} else {"<"}).p(tail).p("; ");
|
||||||
|
y.p("++").p(bind).p(" ) ").p(pass);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Break {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { body } = self;
|
||||||
|
y.nest("break ").p(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Return {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { body } = self;
|
||||||
|
y.nest("return ").p(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Literal {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
match self {
|
||||||
|
Literal::Float(l) => y.p(l),
|
||||||
|
Literal::Bool(l) => y.p(l),
|
||||||
|
Literal::Int(l) => y.p(l),
|
||||||
|
Literal::Char(l) => y.p("'").p(l).p("'"),
|
||||||
|
Literal::String(l) => y.p(&'"').p(l).p(&'"'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Sym {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
y.p(self.to_ref());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Ty {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { span: _, kind, gens: _ } = self;
|
||||||
|
y.p(kind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for TyKind {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
match self {
|
||||||
|
TyKind::Never => y.p("Never"),
|
||||||
|
TyKind::Infer => y.p("auto"),
|
||||||
|
TyKind::Path(t) => y.p(t),
|
||||||
|
TyKind::Tuple(t) => y.p(t),
|
||||||
|
TyKind::Ref(t) => y.p(t),
|
||||||
|
TyKind::Ptr(t) => y.p(t),
|
||||||
|
TyKind::Fn(t) => y.p(t),
|
||||||
|
TyKind::Slice(t) => y.p(t),
|
||||||
|
TyKind::Array(t) => y.p(t),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for Path {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { absolute: _, parts } = self;
|
||||||
|
for (idx, part) in parts.iter().enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
y.p("_");
|
||||||
|
}
|
||||||
|
y.p(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for PathPart {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
match self {
|
||||||
|
PathPart::SuperKw => y.p("super"),
|
||||||
|
PathPart::SelfTy => y.p("Self"),
|
||||||
|
PathPart::Ident(i) => y.p(i),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for TyArray {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { ty, count } = self;
|
||||||
|
y.p(ty).p("[").p(count).p("]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for TySlice {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { ty } = self;
|
||||||
|
y.p(ty).p("* ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for TyTuple {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { types } = self;
|
||||||
|
{
|
||||||
|
let mut y = y.nest("struct {");
|
||||||
|
y.endl();
|
||||||
|
for (idx, ty) in types.iter().enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
y.p(",").endl();
|
||||||
|
}
|
||||||
|
y.p(ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
y.endl().p("}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for TyRef {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { count, mutable, to } = self;
|
||||||
|
y.p(mutable).p(to);
|
||||||
|
for _ in 0..*count {
|
||||||
|
y.p("*");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for TyPtr {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { to } = self;
|
||||||
|
y.p(to).p("*");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for TyFn {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
let Self { args, rety } = self;
|
||||||
|
// TODO: function pointer syntax
|
||||||
|
y.nest("(").p(rety).p(" *)(");
|
||||||
|
match &args.kind {
|
||||||
|
TyKind::Tuple(TyTuple { types }) => {
|
||||||
|
for (idx, ty) in types.iter().enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
y.p(", ");
|
||||||
|
}
|
||||||
|
y.p(ty);
|
||||||
|
}
|
||||||
|
y
|
||||||
|
}
|
||||||
|
_ => y.p(args),
|
||||||
|
}
|
||||||
|
.p(")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: CLangify> CLangify for Option<T> {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
if let Some(v) = self {
|
||||||
|
y.p(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: CLangify> CLangify for Box<T> {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
y.p(&**self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: CLangify> CLangify for Vec<T> {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
for thing in self {
|
||||||
|
y.p(thing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: CLangify> CLangify for [T] {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
for thing in self {
|
||||||
|
y.p(thing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CLangify for () {
|
||||||
|
fn print(&self, _y: &mut CLangifier) {
|
||||||
|
// TODO: C has no language support for zst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: CLangify> CLangify for &T {
|
||||||
|
fn print(&self, y: &mut CLangifier) {
|
||||||
|
(*self).print(y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CLangify for std::fmt::Arguments<'_> {
|
||||||
|
fn print(&self, _y: &mut CLangifier) {
|
||||||
|
print!("{self}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
macro_rules! scalar {
|
||||||
|
($($t:ty),*$(,)?) => {
|
||||||
|
$(impl CLangify for $t {
|
||||||
|
fn print(&self, _y: &mut CLangifier) {
|
||||||
|
print!("{self}");
|
||||||
|
}
|
||||||
|
})*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
scalar! {
|
||||||
|
bool, char, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, str, &str, String
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
//! Pretty prints a conlang AST in yaml
|
//! Pretty prints a conlang AST in yaml
|
||||||
|
|
||||||
|
use cl_ast::Stmt;
|
||||||
use cl_lexer::Lexer;
|
use cl_lexer::Lexer;
|
||||||
use cl_parser::Parser;
|
use cl_parser::Parser;
|
||||||
use cl_repl::repline::{error::Error as RlError, Repline};
|
use repline::{Repline, error::Error as RlError};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
@@ -18,8 +19,8 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
Err(e) => Err(e)?,
|
Err(e) => Err(e)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut parser = Parser::new(Lexer::new(&line));
|
let mut parser = Parser::new("", Lexer::new(&line));
|
||||||
let code = match parser.stmt() {
|
let code = match parser.parse::<Stmt>() {
|
||||||
Ok(code) => {
|
Ok(code) => {
|
||||||
rl.accept();
|
rl.accept();
|
||||||
code
|
code
|
||||||
@@ -40,7 +41,6 @@ pub use yamler::Yamler;
|
|||||||
pub mod yamler {
|
pub mod yamler {
|
||||||
use crate::yamlify::Yamlify;
|
use crate::yamlify::Yamlify;
|
||||||
use std::{
|
use std::{
|
||||||
fmt::Display,
|
|
||||||
io::Write,
|
io::Write,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
};
|
};
|
||||||
@@ -54,7 +54,7 @@ pub mod yamler {
|
|||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn indent(&mut self) -> Section {
|
pub fn indent(&mut self) -> Section<'_> {
|
||||||
Section::new(self)
|
Section::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,28 +80,33 @@ pub mod yamler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Prints a section header and increases indentation
|
/// Prints a section header and increases indentation
|
||||||
pub fn key(&mut self, name: impl Display) -> Section {
|
pub fn key(&mut self, name: impl Yamlify) -> Section<'_> {
|
||||||
println!();
|
println!();
|
||||||
self.print_indentation(&mut std::io::stdout().lock());
|
self.print_indentation(&mut std::io::stdout().lock());
|
||||||
print!("- {name}:");
|
print!(" ");
|
||||||
|
name.yaml(self);
|
||||||
|
print!(":");
|
||||||
self.indent()
|
self.indent()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prints a yaml key value pair: `- name: "value"`
|
/// Prints a yaml key value pair: `- name: "value"`
|
||||||
pub fn pair<D: Display, T: Yamlify>(&mut self, name: D, value: T) -> &mut Self {
|
pub fn pair<D: Yamlify, T: Yamlify>(&mut self, name: D, value: T) -> &mut Self {
|
||||||
self.key(name).yaml(&value);
|
self.key(name).value(value);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prints a yaml scalar value: `"name"``
|
/// Prints a yaml scalar value: `"name"``
|
||||||
pub fn value<D: Display>(&mut self, value: D) -> &mut Self {
|
pub fn value<D: Yamlify>(&mut self, value: D) -> &mut Self {
|
||||||
print!(" {value}");
|
print!(" ");
|
||||||
|
value.yaml(self);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list<D: Yamlify>(&mut self, list: &[D]) -> &mut Self {
|
pub fn list<D: Yamlify>(&mut self, list: &[D]) -> &mut Self {
|
||||||
for (idx, value) in list.iter().enumerate() {
|
for value in list {
|
||||||
self.pair(idx, value);
|
println!();
|
||||||
|
self.print_indentation(&mut std::io::stdout().lock());
|
||||||
|
self.yaml(&"- ").yaml(value);
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -119,19 +124,19 @@ pub mod yamler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'y> Deref for Section<'y> {
|
impl Deref for Section<'_> {
|
||||||
type Target = Yamler;
|
type Target = Yamler;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
self.yamler
|
self.yamler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'y> DerefMut for Section<'y> {
|
impl DerefMut for Section<'_> {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
self.yamler
|
self.yamler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'y> Drop for Section<'y> {
|
impl Drop for Section<'_> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let Self { yamler } = self;
|
let Self { yamler } = self;
|
||||||
yamler.decrease();
|
yamler.decrease();
|
||||||
@@ -149,8 +154,8 @@ pub mod yamlify {
|
|||||||
|
|
||||||
impl Yamlify for File {
|
impl Yamlify for File {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
let File { items } = self;
|
let File { name, items } = self;
|
||||||
y.key("File").yaml(items);
|
y.key("File").pair("name", name).yaml(items);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for Visibility {
|
impl Yamlify for Visibility {
|
||||||
@@ -192,7 +197,7 @@ pub mod yamlify {
|
|||||||
|
|
||||||
impl Yamlify for Item {
|
impl Yamlify for Item {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
let Self { extents: _, attrs, vis, kind } = self;
|
let Self { span: _, attrs, vis, kind } = self;
|
||||||
y.key("Item").yaml(attrs).yaml(vis).yaml(kind);
|
y.key("Item").yaml(attrs).yaml(vis).yaml(kind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,13 +212,20 @@ pub mod yamlify {
|
|||||||
ItemKind::Struct(f) => y.yaml(f),
|
ItemKind::Struct(f) => y.yaml(f),
|
||||||
ItemKind::Enum(f) => y.yaml(f),
|
ItemKind::Enum(f) => y.yaml(f),
|
||||||
ItemKind::Impl(f) => y.yaml(f),
|
ItemKind::Impl(f) => y.yaml(f),
|
||||||
|
ItemKind::Use(f) => y.yaml(f),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Yamlify for Generics {
|
||||||
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
|
let Self { vars } = self;
|
||||||
|
y.key("Generics").value(vars);
|
||||||
|
}
|
||||||
|
}
|
||||||
impl Yamlify for Alias {
|
impl Yamlify for Alias {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
let Self { to, from } = self;
|
let Self { name, from } = self;
|
||||||
y.key("Alias").pair("to", to).pair("from", from);
|
y.key("Alias").pair("to", name).pair("from", from);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for Const {
|
impl Yamlify for Const {
|
||||||
@@ -233,32 +245,28 @@ pub mod yamlify {
|
|||||||
}
|
}
|
||||||
impl Yamlify for Module {
|
impl Yamlify for Module {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
let Self { name, kind } = self;
|
let Self { name, file } = self;
|
||||||
y.key("Module").pair("name", name).yaml(kind);
|
y.key("Module").pair("name", name).yaml(file);
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Yamlify for ModuleKind {
|
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
|
||||||
match self {
|
|
||||||
ModuleKind::Inline(f) => y.yaml(f),
|
|
||||||
ModuleKind::Outline => y,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for Function {
|
impl Yamlify for Function {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
let Self { name, args, body, rety } = self;
|
let Self { name, gens, sign, bind, body } = self;
|
||||||
y.key("Function")
|
y.key("Function")
|
||||||
.pair("name", name)
|
.pair("name", name)
|
||||||
.pair("args", args)
|
.pair("gens", gens)
|
||||||
.pair("body", body)
|
.pair("sign", sign)
|
||||||
.pair("rety", rety);
|
.pair("bind", bind)
|
||||||
|
.pair("body", body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for Struct {
|
impl Yamlify for Struct {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
let Self { name, kind } = self;
|
let Self { name, gens, kind } = self;
|
||||||
y.key("Struct").pair("name", name).yaml(kind);
|
y.key("Struct")
|
||||||
|
.pair("gens", gens)
|
||||||
|
.pair("name", name)
|
||||||
|
.yaml(kind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for StructKind {
|
impl Yamlify for StructKind {
|
||||||
@@ -278,38 +286,56 @@ pub mod yamlify {
|
|||||||
}
|
}
|
||||||
impl Yamlify for Enum {
|
impl Yamlify for Enum {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
let Self { name, kind } = self;
|
let Self { name, gens, variants: kind } = self;
|
||||||
y.key("Enum").pair("name", name).yaml(kind);
|
y.key("Enum")
|
||||||
}
|
.pair("gens", gens)
|
||||||
}
|
.pair("name", name)
|
||||||
impl Yamlify for EnumKind {
|
.yaml(kind);
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
|
||||||
match self {
|
|
||||||
EnumKind::NoVariants => y,
|
|
||||||
EnumKind::Variants(v) => y.yaml(v),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for Variant {
|
impl Yamlify for Variant {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
let Self { name, kind } = self;
|
let Self { name, kind, body } = self;
|
||||||
y.key("Variant").pair("name", name).yaml(kind);
|
y.key("Variant")
|
||||||
}
|
.pair("name", name)
|
||||||
}
|
.pair("kind", kind)
|
||||||
impl Yamlify for VariantKind {
|
.pair("body", body);
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
|
||||||
match self {
|
|
||||||
VariantKind::Plain => y,
|
|
||||||
VariantKind::CLike(v) => y.yaml(v),
|
|
||||||
VariantKind::Tuple(v) => y.yaml(v),
|
|
||||||
VariantKind::Struct(v) => y.yaml(v),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for Impl {
|
impl Yamlify for Impl {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
let Self { target, body } = self;
|
let Self { gens, target, body } = self;
|
||||||
y.key("Impl").pair("target", target).pair("body", body);
|
y.key("Impl")
|
||||||
|
.pair("gens", gens)
|
||||||
|
.pair("target", target)
|
||||||
|
.pair("body", body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Yamlify for ImplKind {
|
||||||
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
|
match self {
|
||||||
|
ImplKind::Type(t) => y.value(t),
|
||||||
|
ImplKind::Trait { impl_trait, for_type } => {
|
||||||
|
y.pair("trait", impl_trait).pair("for_type", for_type)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Yamlify for Use {
|
||||||
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
|
let Self { absolute, tree } = self;
|
||||||
|
y.key("Use").pair("absolute", absolute).yaml(tree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Yamlify for UseTree {
|
||||||
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
|
match self {
|
||||||
|
UseTree::Tree(trees) => y.pair("trees", trees),
|
||||||
|
UseTree::Path(path, tree) => y.pair("path", path).pair("tree", tree),
|
||||||
|
UseTree::Alias(from, to) => y.pair("from", from).pair("to", to),
|
||||||
|
UseTree::Name(name) => y.pair("name", name),
|
||||||
|
UseTree::Glob => y.value("Glob"),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for Block {
|
impl Yamlify for Block {
|
||||||
@@ -320,8 +346,8 @@ pub mod yamlify {
|
|||||||
}
|
}
|
||||||
impl Yamlify for Stmt {
|
impl Yamlify for Stmt {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
let Self { extents: _, kind, semi } = self;
|
let Self { span: _, kind, semi } = self;
|
||||||
y.key("Stmt").yaml(kind).yaml(semi);
|
y.key("Stmt").value(kind).yaml(semi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for Semi {
|
impl Yamlify for Semi {
|
||||||
@@ -335,35 +361,32 @@ pub mod yamlify {
|
|||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
match self {
|
match self {
|
||||||
StmtKind::Empty => y,
|
StmtKind::Empty => y,
|
||||||
StmtKind::Local(s) => y.yaml(s),
|
|
||||||
StmtKind::Item(s) => y.yaml(s),
|
StmtKind::Item(s) => y.yaml(s),
|
||||||
StmtKind::Expr(s) => y.yaml(s),
|
StmtKind::Expr(s) => y.yaml(s),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for Let {
|
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
|
||||||
let Self { mutable, name, ty, init } = self;
|
|
||||||
y.key("Let")
|
|
||||||
.pair("name", name)
|
|
||||||
.yaml(mutable)
|
|
||||||
.pair("ty", ty)
|
|
||||||
.pair("init", init);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Yamlify for Expr {
|
impl Yamlify for Expr {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
let Self { extents: _, kind } = self;
|
let Self { span: _, kind } = self;
|
||||||
y.yaml(kind);
|
y.yaml(kind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for ExprKind {
|
impl Yamlify for ExprKind {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
match self {
|
match self {
|
||||||
|
ExprKind::Closure(k) => k.yaml(y),
|
||||||
|
ExprKind::Quote(k) => k.yaml(y),
|
||||||
|
ExprKind::Let(k) => k.yaml(y),
|
||||||
|
ExprKind::Match(k) => k.yaml(y),
|
||||||
ExprKind::Assign(k) => k.yaml(y),
|
ExprKind::Assign(k) => k.yaml(y),
|
||||||
|
ExprKind::Modify(k) => k.yaml(y),
|
||||||
ExprKind::Binary(k) => k.yaml(y),
|
ExprKind::Binary(k) => k.yaml(y),
|
||||||
ExprKind::Unary(k) => k.yaml(y),
|
ExprKind::Unary(k) => k.yaml(y),
|
||||||
|
ExprKind::Cast(k) => k.yaml(y),
|
||||||
|
ExprKind::Member(k) => k.yaml(y),
|
||||||
ExprKind::Index(k) => k.yaml(y),
|
ExprKind::Index(k) => k.yaml(y),
|
||||||
|
ExprKind::Structor(k) => k.yaml(y),
|
||||||
ExprKind::Path(k) => k.yaml(y),
|
ExprKind::Path(k) => k.yaml(y),
|
||||||
ExprKind::Literal(k) => k.yaml(y),
|
ExprKind::Literal(k) => k.yaml(y),
|
||||||
ExprKind::Array(k) => k.yaml(y),
|
ExprKind::Array(k) => k.yaml(y),
|
||||||
@@ -378,22 +401,99 @@ pub mod yamlify {
|
|||||||
ExprKind::For(k) => k.yaml(y),
|
ExprKind::For(k) => k.yaml(y),
|
||||||
ExprKind::Break(k) => k.yaml(y),
|
ExprKind::Break(k) => k.yaml(y),
|
||||||
ExprKind::Return(k) => k.yaml(y),
|
ExprKind::Return(k) => k.yaml(y),
|
||||||
ExprKind::Continue(k) => k.yaml(y),
|
ExprKind::Continue => {
|
||||||
|
y.key("Continue");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Yamlify for Closure {
|
||||||
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
|
let Self { arg, body } = self;
|
||||||
|
y.key("Closure").pair("arg", arg).pair("body", body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Yamlify for Quote {
|
||||||
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
|
y.key("Quote").value(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Yamlify for Let {
|
||||||
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
|
let Self { mutable, name, ty, init } = self;
|
||||||
|
y.key("Let")
|
||||||
|
.pair("name", name)
|
||||||
|
.yaml(mutable)
|
||||||
|
.pair("ty", ty)
|
||||||
|
.pair("init", init);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Yamlify for Pattern {
|
||||||
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
|
match self {
|
||||||
|
Pattern::Name(name) => y.value(name),
|
||||||
|
Pattern::Path(path) => y.value(path),
|
||||||
|
Pattern::Literal(literal) => y.value(literal),
|
||||||
|
Pattern::Rest(name) => y.pair("Rest", name),
|
||||||
|
Pattern::Ref(mutability, pattern) => y.yaml(mutability).pair("Pat", pattern),
|
||||||
|
Pattern::RangeInc(head, tail) => {
|
||||||
|
y.key("RangeInc").pair("head", head).pair("tail", tail);
|
||||||
|
y
|
||||||
|
}
|
||||||
|
Pattern::RangeExc(head, tail) => {
|
||||||
|
y.key("RangeExc").pair("head", head).pair("tail", tail);
|
||||||
|
y
|
||||||
|
}
|
||||||
|
Pattern::Tuple(patterns) => y.key("Tuple").list(patterns),
|
||||||
|
Pattern::Array(patterns) => y.key("Array").list(patterns),
|
||||||
|
Pattern::Struct(path, items) => {
|
||||||
|
{
|
||||||
|
let mut y = y.key("Struct");
|
||||||
|
y.yaml(path);
|
||||||
|
for (name, item) in items {
|
||||||
|
y.pair(name, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
y
|
||||||
|
}
|
||||||
|
Pattern::TupleStruct(path, items) => {
|
||||||
|
y.key("TupleStruct").yaml(path).list(items);
|
||||||
|
y
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Yamlify for Match {
|
||||||
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
|
let Self { scrutinee, arms } = self;
|
||||||
|
y.key("Match")
|
||||||
|
.pair("scrutinee", scrutinee)
|
||||||
|
.pair("arms", arms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Yamlify for MatchArm {
|
||||||
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
|
let Self(pat, expr) = self;
|
||||||
|
y.pair("pat", pat).pair("expr", expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
impl Yamlify for Assign {
|
impl Yamlify for Assign {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
let Self { kind, parts } = self;
|
let Self { parts } = self;
|
||||||
y.key("Assign")
|
y.key("Assign")
|
||||||
.pair("kind", kind)
|
|
||||||
.pair("head", &parts.0)
|
.pair("head", &parts.0)
|
||||||
.pair("tail", &parts.1);
|
.pair("tail", &parts.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for AssignKind {
|
impl Yamlify for Modify {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
y.value(self);
|
let Self { kind, parts } = self;
|
||||||
|
y.key("Modify")
|
||||||
|
.pair("kind", kind)
|
||||||
|
.pair("head", &parts.0)
|
||||||
|
.pair("tail", &parts.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for Binary {
|
impl Yamlify for Binary {
|
||||||
@@ -405,20 +505,31 @@ pub mod yamlify {
|
|||||||
.pair("tail", &parts.1);
|
.pair("tail", &parts.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for BinaryKind {
|
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
|
||||||
y.value(self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Yamlify for Unary {
|
impl Yamlify for Unary {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
let Self { kind, tail } = self;
|
let Self { kind, tail } = self;
|
||||||
y.key("Unary").pair("kind", kind).pair("tail", tail);
|
y.key("Unary").pair("kind", kind).pair("tail", tail);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for UnaryKind {
|
impl Yamlify for Cast {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
y.value(self);
|
let Self { head, ty } = self;
|
||||||
|
y.key("Cast").pair("head", head).pair("ty", ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Yamlify for Member {
|
||||||
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
|
let Self { head, kind } = self;
|
||||||
|
y.key("Member").pair("head", head).pair("kind", kind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Yamlify for MemberKind {
|
||||||
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
|
match self {
|
||||||
|
MemberKind::Call(id, args) => y.pair("id", id).pair("args", args),
|
||||||
|
MemberKind::Struct(id) => y.pair("id", id),
|
||||||
|
MemberKind::Tuple(id) => y.pair("id", id),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for Tuple {
|
impl Yamlify for Tuple {
|
||||||
@@ -430,7 +541,22 @@ pub mod yamlify {
|
|||||||
impl Yamlify for Index {
|
impl Yamlify for Index {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
let Self { head, indices } = self;
|
let Self { head, indices } = self;
|
||||||
y.key("Index").pair("head", head).list(indices);
|
y.key("Index")
|
||||||
|
.pair("head", head)
|
||||||
|
.key("indices")
|
||||||
|
.list(indices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Yamlify for Structor {
|
||||||
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
|
let Self { to, init } = self;
|
||||||
|
y.key("Structor").pair("to", to).list(init);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Yamlify for Fielder {
|
||||||
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
|
let Self { name, init } = self;
|
||||||
|
y.key("Fielder").pair("name", name).pair("init", init);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for Array {
|
impl Yamlify for Array {
|
||||||
@@ -449,11 +575,8 @@ pub mod yamlify {
|
|||||||
}
|
}
|
||||||
impl Yamlify for AddrOf {
|
impl Yamlify for AddrOf {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
let Self { count, mutable, expr } = self;
|
let Self { mutable, expr } = self;
|
||||||
y.key("AddrOf")
|
y.key("AddrOf").yaml(mutable).pair("expr", expr);
|
||||||
.yaml(mutable)
|
|
||||||
.pair("count", count)
|
|
||||||
.pair("expr", expr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for Group {
|
impl Yamlify for Group {
|
||||||
@@ -468,13 +591,13 @@ pub mod yamlify {
|
|||||||
y.key("While")
|
y.key("While")
|
||||||
.pair("cond", cond)
|
.pair("cond", cond)
|
||||||
.pair("pass", pass)
|
.pair("pass", pass)
|
||||||
.pair("fail", fail);
|
.yaml(fail);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for Else {
|
impl Yamlify for Else {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
let Self { body } = self;
|
let Self { body } = self;
|
||||||
y.key("Else").yaml(body);
|
y.key("fail").yaml(body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for If {
|
impl Yamlify for If {
|
||||||
@@ -505,47 +628,35 @@ pub mod yamlify {
|
|||||||
y.key("Return").yaml(body);
|
y.key("Return").yaml(body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for Continue {
|
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
|
||||||
y.key("Continue");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Yamlify for Literal {
|
impl Yamlify for Literal {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, _y: &mut Yamler) {
|
||||||
y.value(format_args!("\"{self}\""));
|
match self {
|
||||||
}
|
Literal::Bool(v) => print!("{v}"),
|
||||||
}
|
Literal::Char(v) => print!("'{}'", v.escape_debug()),
|
||||||
impl Yamlify for Identifier {
|
Literal::Int(v) => print!("{v}"),
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
Literal::Float(v) => print!("{v}"),
|
||||||
let Self(name) = self;
|
Literal::String(v) => print!("{}", v.escape_debug()),
|
||||||
y.value(name);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Yamlify for Param {
|
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
|
||||||
let Self { mutability, name, ty } = self;
|
|
||||||
y.key("Param")
|
|
||||||
.yaml(mutability)
|
|
||||||
.pair("name", name)
|
|
||||||
.pair("ty", ty);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for Ty {
|
impl Yamlify for Ty {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
let Self { extents: _, kind } = self;
|
let Self { span: _, kind, gens } = self;
|
||||||
y.key("Ty").yaml(kind);
|
y.key("Ty").yaml(kind).yaml(gens);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for TyKind {
|
impl Yamlify for TyKind {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
match self {
|
match self {
|
||||||
TyKind::Never => y.value("Never"),
|
TyKind::Never => y.value("Never"),
|
||||||
TyKind::Empty => y.value("Empty"),
|
TyKind::Infer => y.value("_"),
|
||||||
TyKind::SelfTy => y.value("Self"),
|
|
||||||
TyKind::Path(t) => y.yaml(t),
|
TyKind::Path(t) => y.yaml(t),
|
||||||
TyKind::Tuple(t) => y.yaml(t),
|
TyKind::Tuple(t) => y.yaml(t),
|
||||||
TyKind::Ref(t) => y.yaml(t),
|
TyKind::Ref(t) => y.yaml(t),
|
||||||
|
TyKind::Ptr(t) => y.yaml(t),
|
||||||
TyKind::Fn(t) => y.yaml(t),
|
TyKind::Fn(t) => y.yaml(t),
|
||||||
|
TyKind::Slice(t) => y.yaml(t),
|
||||||
|
TyKind::Array(t) => y.yaml(t),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -556,20 +667,30 @@ pub mod yamlify {
|
|||||||
if *absolute {
|
if *absolute {
|
||||||
y.pair("absolute", absolute);
|
y.pair("absolute", absolute);
|
||||||
}
|
}
|
||||||
for part in parts {
|
y.yaml(parts);
|
||||||
y.pair("part", part);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for PathPart {
|
impl Yamlify for PathPart {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
match self {
|
match self {
|
||||||
PathPart::SuperKw => y.value("super"),
|
PathPart::SuperKw => y.value("super"),
|
||||||
PathPart::SelfKw => y.value("self"),
|
PathPart::SelfTy => y.value("Self"),
|
||||||
PathPart::Ident(i) => y.yaml(i),
|
PathPart::Ident(i) => y.yaml(i),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Yamlify for TyArray {
|
||||||
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
|
let Self { ty, count } = self;
|
||||||
|
y.key("TyArray").pair("ty", ty).pair("count", count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Yamlify for TySlice {
|
||||||
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
|
let Self { ty } = self;
|
||||||
|
y.key("TyArray").pair("ty", ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
impl Yamlify for TyTuple {
|
impl Yamlify for TyTuple {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
let Self { types } = self;
|
let Self { types } = self;
|
||||||
@@ -581,8 +702,17 @@ pub mod yamlify {
|
|||||||
}
|
}
|
||||||
impl Yamlify for TyRef {
|
impl Yamlify for TyRef {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
let Self { count, to } = self;
|
let Self { count, mutable, to } = self;
|
||||||
y.key("TyRef").pair("count", count).pair("to", to);
|
y.key("TyRef")
|
||||||
|
.pair("count", count)
|
||||||
|
.yaml(mutable)
|
||||||
|
.pair("to", to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Yamlify for TyPtr {
|
||||||
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
|
let Self { to } = self;
|
||||||
|
y.key("TyPtr").pair("to", to);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for TyFn {
|
impl Yamlify for TyFn {
|
||||||
@@ -608,9 +738,7 @@ pub mod yamlify {
|
|||||||
}
|
}
|
||||||
impl<T: Yamlify> Yamlify for Vec<T> {
|
impl<T: Yamlify> Yamlify for Vec<T> {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, y: &mut Yamler) {
|
||||||
for thing in self {
|
y.list(self);
|
||||||
y.yaml(thing);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Yamlify for () {
|
impl Yamlify for () {
|
||||||
@@ -626,14 +754,15 @@ pub mod yamlify {
|
|||||||
macro_rules! scalar {
|
macro_rules! scalar {
|
||||||
($($t:ty),*$(,)?) => {
|
($($t:ty),*$(,)?) => {
|
||||||
$(impl Yamlify for $t {
|
$(impl Yamlify for $t {
|
||||||
fn yaml(&self, y: &mut Yamler) {
|
fn yaml(&self, _y: &mut Yamler) {
|
||||||
y.value(self);
|
print!("{self}");
|
||||||
}
|
}
|
||||||
})*
|
})*
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
scalar! {
|
scalar! {
|
||||||
bool, char, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, &str, String
|
bool, char, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, &str, String,
|
||||||
|
BinaryKind, UnaryKind, ModifyKind, Sym,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
14
compiler/cl-repl/src/ansi.rs
Normal file
14
compiler/cl-repl/src/ansi.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
//! ANSI escape sequences
|
||||||
|
|
||||||
|
pub const RED: &str = "\x1b[31m";
|
||||||
|
pub const GREEN: &str = "\x1b[32m"; // the color of type checker mode
|
||||||
|
pub const CYAN: &str = "\x1b[36m";
|
||||||
|
pub const BRIGHT_GREEN: &str = "\x1b[92m";
|
||||||
|
pub const BRIGHT_BLUE: &str = "\x1b[94m";
|
||||||
|
pub const BRIGHT_MAGENTA: &str = "\x1b[95m";
|
||||||
|
pub const BRIGHT_CYAN: &str = "\x1b[96m";
|
||||||
|
pub const RESET: &str = "\x1b[0m";
|
||||||
|
pub const OUTPUT: &str = "\x1b[38;5;117m";
|
||||||
|
|
||||||
|
pub const CLEAR_LINES: &str = "\x1b[G\x1b[J";
|
||||||
|
pub const CLEAR_ALL: &str = "\x1b[H\x1b[2J\x1b[3J";
|
||||||
68
compiler/cl-repl/src/args.rs
Normal file
68
compiler/cl-repl/src/args.rs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
//! Handles argument parsing (currently using the [argwerk] crate)
|
||||||
|
|
||||||
|
use std::{io::IsTerminal, path::PathBuf, str::FromStr};
|
||||||
|
|
||||||
|
argwerk::define! {
|
||||||
|
///
|
||||||
|
///The Conlang prototype debug interface
|
||||||
|
#[usage = "conlang [<file>] [-I <include...>] [-m <mode>] [-r <repl>]"]
|
||||||
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct Args {
|
||||||
|
pub file: Option<PathBuf>,
|
||||||
|
pub include: Vec<PathBuf>,
|
||||||
|
pub mode: Mode,
|
||||||
|
pub repl: bool = is_terminal(),
|
||||||
|
}
|
||||||
|
|
||||||
|
///files to include
|
||||||
|
["-I" | "--include", path] => {
|
||||||
|
include.push(path.into());
|
||||||
|
}
|
||||||
|
///the CLI operating mode (`f`mt | `l`ex | `r`un)
|
||||||
|
["-m" | "--mode", flr] => {
|
||||||
|
mode = flr.parse()?;
|
||||||
|
}
|
||||||
|
///whether to start the repl (`true` or `false`)
|
||||||
|
["-r" | "--repl", bool] => {
|
||||||
|
repl = bool.parse()?;
|
||||||
|
}
|
||||||
|
///display usage information
|
||||||
|
["-h" | "--help"] => {
|
||||||
|
println!("{}", Args::help());
|
||||||
|
if true { std::process::exit(0); }
|
||||||
|
}
|
||||||
|
///the main source file
|
||||||
|
[#[option] path] if file.is_none() => {
|
||||||
|
file = path.map(Into::into);
|
||||||
|
}
|
||||||
|
|
||||||
|
[path] if file.is_some() => {
|
||||||
|
include.push(path.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// gets whether stdin AND stdout are a terminal, for pipelining
|
||||||
|
pub fn is_terminal() -> bool {
|
||||||
|
std::io::stdin().is_terminal() && std::io::stdout().is_terminal()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The CLI's operating mode
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub enum Mode {
|
||||||
|
Lex,
|
||||||
|
Fmt,
|
||||||
|
#[default]
|
||||||
|
Run,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Mode {
|
||||||
|
type Err = &'static str;
|
||||||
|
fn from_str(s: &str) -> Result<Self, &'static str> {
|
||||||
|
Ok(match s {
|
||||||
|
"f" | "fmt" | "p" | "pretty" => Mode::Fmt,
|
||||||
|
"l" | "lex" | "tokenize" | "token" => Mode::Lex,
|
||||||
|
"r" | "run" => Mode::Run,
|
||||||
|
_ => Err("Recognized modes are: 'r' \"run\", 'f' \"fmt\", 'l' \"lex\"")?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
5
compiler/cl-repl/src/bin/conlang.rs
Normal file
5
compiler/cl-repl/src/bin/conlang.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
use cl_repl::{args, cli::run};
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
run(args::Args::args()?)
|
||||||
|
}
|
||||||
171
compiler/cl-repl/src/cli.rs
Normal file
171
compiler/cl-repl/src/cli.rs
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
//! Implement's the command line interface
|
||||||
|
use crate::{
|
||||||
|
args::{Args, Mode},
|
||||||
|
ctx::Context,
|
||||||
|
menu,
|
||||||
|
tools::print_token,
|
||||||
|
};
|
||||||
|
use cl_ast::File;
|
||||||
|
use cl_interpret::{builtin::builtins, convalue::ConValue, env::Environment, interpret::Interpret};
|
||||||
|
use cl_lexer::Lexer;
|
||||||
|
use cl_parser::Parser;
|
||||||
|
use std::{borrow::Cow, error::Error, path::Path};
|
||||||
|
|
||||||
|
/// Run the command line interface
|
||||||
|
pub fn run(args: Args) -> Result<(), Box<dyn Error>> {
|
||||||
|
let Args { file, include, mode, repl } = args;
|
||||||
|
|
||||||
|
let mut env = Environment::new();
|
||||||
|
|
||||||
|
env.add_builtins(&builtins! {
|
||||||
|
/// Lexes, parses, and evaluates an expression in the current env
|
||||||
|
fn eval(string) @env {
|
||||||
|
use cl_interpret::error::Error;
|
||||||
|
let string = match string {
|
||||||
|
ConValue::Str(string) => string.to_ref(),
|
||||||
|
ConValue::String(string) => string.as_str(),
|
||||||
|
ConValue::Ref(v) => {
|
||||||
|
let string = env.get_id(*v).cloned().unwrap_or_default();
|
||||||
|
return eval(env, &[string])
|
||||||
|
}
|
||||||
|
_ => Err(Error::TypeError())?
|
||||||
|
};
|
||||||
|
match Parser::new("eval", Lexer::new(string)).parse::<cl_ast::Stmt>() {
|
||||||
|
Err(e) => Ok(ConValue::Str(format!("{e}").into())),
|
||||||
|
Ok(v) => v.interpret(env),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Executes a file
|
||||||
|
fn import(path) @env {
|
||||||
|
use cl_interpret::error::Error;
|
||||||
|
match path {
|
||||||
|
ConValue::Str(path) => load_file(env, &**path).or(Ok(ConValue::Empty)),
|
||||||
|
ConValue::String(path) => load_file(env, &**path).or(Ok(ConValue::Empty)),
|
||||||
|
_ => Err(Error::TypeError())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn putchar(ConValue::Char(c)) {
|
||||||
|
print!("{c}");
|
||||||
|
Ok(ConValue::Empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a line of input from stdin
|
||||||
|
fn get_line(prompt) {
|
||||||
|
use cl_interpret::error::Error;
|
||||||
|
let prompt = match prompt {
|
||||||
|
ConValue::Str(prompt) => prompt.to_ref(),
|
||||||
|
ConValue::String(prompt) => prompt.as_str(),
|
||||||
|
_ => Err(Error::TypeError())?,
|
||||||
|
};
|
||||||
|
match repline::Repline::new("", prompt, "").read() {
|
||||||
|
Ok(line) => Ok(ConValue::String(line)),
|
||||||
|
Err(repline::Error::CtrlD(line)) => Ok(ConValue::String(line)),
|
||||||
|
Err(repline::Error::CtrlC(_)) => Err(cl_interpret::error::Error::Break(ConValue::Empty)),
|
||||||
|
Err(e) => Ok(ConValue::Str(e.to_string().into())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for path in include {
|
||||||
|
load_file(&mut env, path)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if repl {
|
||||||
|
if let Some(file) = file
|
||||||
|
&& let Err(e) = load_file(&mut env, file)
|
||||||
|
{
|
||||||
|
eprintln!("{e}")
|
||||||
|
}
|
||||||
|
let mut ctx = Context::with_env(env);
|
||||||
|
menu::main_menu(mode, &mut ctx)?;
|
||||||
|
} else {
|
||||||
|
let path = format_path_for_display(file.as_deref());
|
||||||
|
let code = match &file {
|
||||||
|
Some(file) => std::fs::read_to_string(file)?,
|
||||||
|
None => std::io::read_to_string(std::io::stdin())?,
|
||||||
|
};
|
||||||
|
|
||||||
|
match mode {
|
||||||
|
Mode::Lex => lex_code(&path, &code),
|
||||||
|
Mode::Fmt => fmt_code(&path, &code),
|
||||||
|
Mode::Run => run_code(&path, &code, &mut env),
|
||||||
|
}?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_path_for_display(path: Option<&Path>) -> Cow<'_, str> {
|
||||||
|
match path {
|
||||||
|
Some(file) => file
|
||||||
|
.to_str()
|
||||||
|
.map(Cow::Borrowed)
|
||||||
|
.unwrap_or_else(|| Cow::Owned(file.display().to_string())),
|
||||||
|
None => Cow::Borrowed(""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_file(env: &mut Environment, path: impl AsRef<Path>) -> Result<ConValue, Box<dyn Error>> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let inliner = cl_parser::inliner::ModuleInliner::new(path.with_extension(""));
|
||||||
|
let file = std::fs::read_to_string(path)?;
|
||||||
|
let code = Parser::new(path.display().to_string(), Lexer::new(&file)).parse()?;
|
||||||
|
let code = match inliner.inline(code) {
|
||||||
|
Ok(a) => a,
|
||||||
|
Err((code, io_errs, parse_errs)) => {
|
||||||
|
for (file, err) in io_errs {
|
||||||
|
eprintln!("{}:{err}", file.display());
|
||||||
|
}
|
||||||
|
for (file, err) in parse_errs {
|
||||||
|
eprintln!("{}:{err}", file.display());
|
||||||
|
}
|
||||||
|
code
|
||||||
|
}
|
||||||
|
};
|
||||||
|
use cl_ast::WeightOf;
|
||||||
|
eprintln!("File {} weighs {} units", code.name, code.weight_of());
|
||||||
|
|
||||||
|
match env.eval(&code) {
|
||||||
|
Ok(v) => Ok(v),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{e}");
|
||||||
|
Ok(ConValue::Empty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lex_code(path: &str, code: &str) -> Result<(), Box<dyn Error>> {
|
||||||
|
for token in Lexer::new(code) {
|
||||||
|
if !path.is_empty() {
|
||||||
|
print!("{}:", path);
|
||||||
|
}
|
||||||
|
match token {
|
||||||
|
Ok(token) => print_token(&token),
|
||||||
|
Err(e) => println!("{e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_code(path: &str, code: &str) -> Result<(), Box<dyn Error>> {
|
||||||
|
let code = Parser::new(path, Lexer::new(code)).parse::<File>()?;
|
||||||
|
println!("{code}");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_code(path: &str, code: &str, env: &mut Environment) -> Result<(), Box<dyn Error>> {
|
||||||
|
let code = Parser::new(path, Lexer::new(code)).parse::<File>()?;
|
||||||
|
match code.interpret(env)? {
|
||||||
|
ConValue::Empty => {}
|
||||||
|
ret => println!("{ret}"),
|
||||||
|
}
|
||||||
|
if env.get("main".into()).is_ok() {
|
||||||
|
match env.call("main".into(), &[]) {
|
||||||
|
Ok(ConValue::Empty) => {}
|
||||||
|
Ok(ret) => println!("{ret}"),
|
||||||
|
Err(e) => println!("Error: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
24
compiler/cl-repl/src/ctx.rs
Normal file
24
compiler/cl-repl/src/ctx.rs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
use cl_interpret::{convalue::ConValue, env::Environment, error::IResult, interpret::Interpret};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Context {
|
||||||
|
pub env: Environment,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { env: Environment::new() }
|
||||||
|
}
|
||||||
|
pub fn with_env(env: Environment) -> Self {
|
||||||
|
Self { env }
|
||||||
|
}
|
||||||
|
pub fn run(&mut self, code: &impl Interpret) -> IResult<ConValue> {
|
||||||
|
code.interpret(&mut self.env)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Context {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
11
compiler/cl-repl/src/lib.rs
Normal file
11
compiler/cl-repl/src/lib.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
//! The Conlang REPL, based on [repline]
|
||||||
|
//!
|
||||||
|
//! Uses [argwerk] for argument parsing.
|
||||||
|
#![warn(clippy::all)]
|
||||||
|
|
||||||
|
pub mod ansi;
|
||||||
|
pub mod args;
|
||||||
|
pub mod cli;
|
||||||
|
pub mod ctx;
|
||||||
|
pub mod menu;
|
||||||
|
pub mod tools;
|
||||||
129
compiler/cl-repl/src/menu.rs
Normal file
129
compiler/cl-repl/src/menu.rs
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
use crate::{ansi, args::Mode, ctx};
|
||||||
|
use cl_ast::Stmt;
|
||||||
|
use cl_interpret::convalue::ConValue;
|
||||||
|
use cl_lexer::Lexer;
|
||||||
|
use cl_parser::Parser;
|
||||||
|
use repline::{Error as RlError, error::ReplResult, prebaked::*};
|
||||||
|
|
||||||
|
pub fn clear() {
|
||||||
|
print!("{}", ansi::CLEAR_ALL);
|
||||||
|
banner()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn banner() {
|
||||||
|
println!("--- conlang v{} 💪🦈 ---", env!("CARGO_PKG_VERSION"))
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReplCallback = fn(&mut ctx::Context, &str) -> Result<Response, Box<dyn Error>>;
|
||||||
|
type ReplMode = (&'static str, &'static str, &'static str, ReplCallback);
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
const MODES: &[ReplMode] = &[
|
||||||
|
(ansi::CYAN, " .>", " >", mode_run),
|
||||||
|
(ansi::BRIGHT_BLUE, " .>", " >", mode_lex),
|
||||||
|
(ansi::BRIGHT_MAGENTA, " .>", " >", mode_fmt),
|
||||||
|
];
|
||||||
|
|
||||||
|
const fn get_mode(mode: Mode) -> ReplMode {
|
||||||
|
match mode {
|
||||||
|
Mode::Lex => MODES[1],
|
||||||
|
Mode::Fmt => MODES[2],
|
||||||
|
Mode::Run => MODES[0],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Presents a selection interface to the user
|
||||||
|
pub fn main_menu(mode: Mode, ctx: &mut ctx::Context) -> ReplResult<()> {
|
||||||
|
let mut mode = get_mode(mode);
|
||||||
|
banner();
|
||||||
|
|
||||||
|
let mut rl = repline::Repline::new(mode.0, mode.1, mode.2);
|
||||||
|
loop {
|
||||||
|
rl.set_prompt(mode.0, mode.1, mode.2);
|
||||||
|
|
||||||
|
let line = match rl.read() {
|
||||||
|
Err(RlError::CtrlC(_)) => return Ok(()),
|
||||||
|
Err(RlError::CtrlD(line)) => {
|
||||||
|
rl.deny();
|
||||||
|
line
|
||||||
|
}
|
||||||
|
Ok(line) => line,
|
||||||
|
Err(e) => Err(e)?,
|
||||||
|
};
|
||||||
|
print!("\x1b[G\x1b[J");
|
||||||
|
match line.trim() {
|
||||||
|
"" => continue,
|
||||||
|
"clear" => clear(),
|
||||||
|
"mode run" => mode = get_mode(Mode::Run),
|
||||||
|
"mode lex" => mode = get_mode(Mode::Lex),
|
||||||
|
"mode fmt" => mode = get_mode(Mode::Fmt),
|
||||||
|
"quit" => return Ok(()),
|
||||||
|
"help" => println!(
|
||||||
|
"Valid commands
|
||||||
|
help : Print this list
|
||||||
|
clear : Clear the screen
|
||||||
|
quit : Exit the program
|
||||||
|
mode lex : Lex the input
|
||||||
|
mode fmt : Format the input
|
||||||
|
mode run : Evaluate some expressions"
|
||||||
|
),
|
||||||
|
_ => match mode.3(ctx, &line) {
|
||||||
|
Ok(Response::Continue) => continue,
|
||||||
|
Ok(Response::Break) => return Ok(()),
|
||||||
|
Ok(Response::Deny) => {}
|
||||||
|
Ok(Response::Accept) => {
|
||||||
|
rl.accept();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
rl.print_inline(format_args!("\t\x1b[31m{e}\x1b[0m"))?;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
rl.deny();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mode_run(ctx: &mut ctx::Context, line: &str) -> Result<Response, Box<dyn Error>> {
|
||||||
|
use cl_ast::ast_visitor::Fold;
|
||||||
|
use cl_parser::inliner::ModuleInliner;
|
||||||
|
|
||||||
|
if line.trim().is_empty() {
|
||||||
|
return Ok(Response::Deny);
|
||||||
|
}
|
||||||
|
let code = Parser::new("", Lexer::new(line)).parse::<Stmt>()?;
|
||||||
|
let code = ModuleInliner::new(".").fold_stmt(code);
|
||||||
|
|
||||||
|
print!("{}", ansi::OUTPUT);
|
||||||
|
match ctx.run(&code) {
|
||||||
|
Ok(ConValue::Empty) => print!("{}", ansi::RESET),
|
||||||
|
Ok(v) => println!("{}{v}", ansi::RESET),
|
||||||
|
Err(e) => println!("{}! > {e}{}", ansi::RED, ansi::RESET),
|
||||||
|
}
|
||||||
|
Ok(Response::Accept)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mode_lex(_ctx: &mut ctx::Context, line: &str) -> Result<Response, Box<dyn Error>> {
|
||||||
|
for token in Lexer::new(line) {
|
||||||
|
match token {
|
||||||
|
Ok(token) => crate::tools::print_token(&token),
|
||||||
|
Err(e) => eprintln!("! > {}{e}{}", ansi::RED, ansi::RESET),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Response::Accept)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mode_fmt(_ctx: &mut ctx::Context, line: &str) -> Result<Response, Box<dyn Error>> {
|
||||||
|
let mut p = Parser::new("", Lexer::new(line));
|
||||||
|
|
||||||
|
match p.parse::<Stmt>() {
|
||||||
|
Ok(code) => println!("{}{code}{}", ansi::OUTPUT, ansi::RESET),
|
||||||
|
Err(e) => Err(e)?,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Response::Accept)
|
||||||
|
}
|
||||||
11
compiler/cl-repl/src/tools.rs
Normal file
11
compiler/cl-repl/src/tools.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
use cl_token::Token;
|
||||||
|
/// Prints a token in the particular way [cl-repl](crate) does
|
||||||
|
pub fn print_token(t: &Token) {
|
||||||
|
println!(
|
||||||
|
"{:02}:{:02}: {:#19} │{}│",
|
||||||
|
t.line(),
|
||||||
|
t.col(),
|
||||||
|
t.ty(),
|
||||||
|
t.data(),
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -8,3 +8,4 @@ license.workspace = true
|
|||||||
publish.workspace = true
|
publish.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
cl-arena = { version = "0", registry = "soft-fish" }
|
||||||
219
compiler/cl-structures/src/index_map.rs
Normal file
219
compiler/cl-structures/src/index_map.rs
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
//! Trivially-copyable, easily comparable typed [indices](MapIndex),
|
||||||
|
//! and an [IndexMap] to contain them.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! # use cl_structures::index_map::*;
|
||||||
|
//! // first, create a new MapIndex type (this ensures type safety)
|
||||||
|
//! make_index! {
|
||||||
|
//! Number
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // then, create a map with that type
|
||||||
|
//! let mut numbers: IndexMap<Number, i32> = IndexMap::new();
|
||||||
|
//! let first = numbers.insert(1);
|
||||||
|
//! let second = numbers.insert(2);
|
||||||
|
//! let third = numbers.insert(3);
|
||||||
|
//!
|
||||||
|
//! // You can access elements immutably with `get`
|
||||||
|
//! assert_eq!(Some(&3), numbers.get(third));
|
||||||
|
//! assert_eq!(Some(&2), numbers.get(second));
|
||||||
|
//! // or by indexing
|
||||||
|
//! assert_eq!(1, numbers[first]);
|
||||||
|
//!
|
||||||
|
//! // Or mutably
|
||||||
|
//! *numbers.get_mut(first).unwrap() = 100000;
|
||||||
|
//!
|
||||||
|
//! assert_eq!(Some(&100000), numbers.get(first));
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
/// Creates newtype indices over [`usize`] for use as [IndexMap] keys.
|
||||||
|
///
|
||||||
|
/// Generated key types implement [Clone], [Copy],
|
||||||
|
/// [Debug](core::fmt::Debug), [PartialEq], [Eq], [PartialOrd], [Ord], [Hash](core::hash::Hash),
|
||||||
|
/// and [MapIndex].
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! make_index {($($(#[$meta:meta])* $name:ident),*$(,)?) => {$(
|
||||||
|
$(#[$meta])*
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct $name(usize);
|
||||||
|
|
||||||
|
impl $crate::index_map::MapIndex for $name {
|
||||||
|
#[doc = concat!("Constructs a [`", stringify!($name), "`] from a [`usize`] without checking bounds.\n")]
|
||||||
|
/// The provided value should be within the bounds of its associated container
|
||||||
|
#[inline]
|
||||||
|
fn from_usize(value: usize) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn get(&self) -> usize {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From< $name > for usize {
|
||||||
|
fn from(value: $name) -> Self {
|
||||||
|
value.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*}}
|
||||||
|
|
||||||
|
use self::iter::MapIndexIter;
|
||||||
|
use std::{
|
||||||
|
ops::{Index, IndexMut},
|
||||||
|
slice::GetDisjointMutError,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use make_index;
|
||||||
|
|
||||||
|
/// An index into a [IndexMap]. For full type-safety,
|
||||||
|
/// there should be a unique [MapIndex] for each [IndexMap].
|
||||||
|
pub trait MapIndex: std::fmt::Debug {
|
||||||
|
/// Constructs an [`MapIndex`] from a [`usize`] without checking bounds.
|
||||||
|
///
|
||||||
|
/// The provided value should be within the bounds of its associated container.
|
||||||
|
fn from_usize(value: usize) -> Self;
|
||||||
|
/// Gets the index of the [`MapIndex`] by value
|
||||||
|
fn get(&self) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// It's an array. Lmao.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct IndexMap<K: MapIndex, V> {
|
||||||
|
map: Vec<V>,
|
||||||
|
id_type: std::marker::PhantomData<K>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V, K: MapIndex> IndexMap<K, V> {
|
||||||
|
/// Constructs an empty IndexMap.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a reference to the value in slot `index`.
|
||||||
|
pub fn get(&self, index: K) -> Option<&V> {
|
||||||
|
self.map.get(index.get())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a mutable reference to the value in slot `index`.
|
||||||
|
pub fn get_mut(&mut self, index: K) -> Option<&mut V> {
|
||||||
|
self.map.get_mut(index.get())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns mutable references to many indices at once.
|
||||||
|
///
|
||||||
|
/// Returns an error if any index is out of bounds, or if the same index was passed twice.
|
||||||
|
pub fn get_disjoint_mut<const N: usize>(
|
||||||
|
&mut self,
|
||||||
|
indices: [K; N],
|
||||||
|
) -> Result<[&mut V; N], GetDisjointMutError> {
|
||||||
|
self.map.get_disjoint_mut(indices.map(|id| id.get()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator over the IndexMap.
|
||||||
|
pub fn values(&self) -> impl Iterator<Item = &V> {
|
||||||
|
self.map.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator that allows modifying each value.
|
||||||
|
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> {
|
||||||
|
self.map.iter_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator over all keys in the IndexMap.
|
||||||
|
pub fn keys(&self) -> iter::MapIndexIter<K> {
|
||||||
|
// Safety: IndexMap currently has map.len() entries, and data cannot be removed
|
||||||
|
MapIndexIter::new(0..self.map.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs an [ID](MapIndex) from a [usize], if it's within bounds
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn try_key_from(&self, value: usize) -> Option<K> {
|
||||||
|
(value < self.map.len()).then(|| K::from_usize(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts a new item into the IndexMap, returning the key associated with it.
|
||||||
|
pub fn insert(&mut self, value: V) -> K {
|
||||||
|
let id = self.map.len();
|
||||||
|
self.map.push(value);
|
||||||
|
|
||||||
|
// Safety: value was pushed to `self.map[id]`
|
||||||
|
K::from_usize(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replaces a value in the IndexMap, returning the old value.
|
||||||
|
pub fn replace(&mut self, key: K, value: V) -> V {
|
||||||
|
std::mem::replace(&mut self[key], value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: MapIndex, V> Default for IndexMap<K, V> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { map: vec![], id_type: std::marker::PhantomData }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: MapIndex, V> Index<K> for IndexMap<K, V> {
|
||||||
|
type Output = V;
|
||||||
|
|
||||||
|
fn index(&self, index: K) -> &Self::Output {
|
||||||
|
match self.map.get(index.get()) {
|
||||||
|
None => panic!("Index {:?} out of bounds in IndexMap!", index),
|
||||||
|
Some(value) => value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: MapIndex, V> IndexMut<K> for IndexMap<K, V> {
|
||||||
|
fn index_mut(&mut self, index: K) -> &mut Self::Output {
|
||||||
|
match self.map.get_mut(index.get()) {
|
||||||
|
None => panic!("Index {:?} out of bounds in IndexMap!", index),
|
||||||
|
Some(value) => value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod iter {
|
||||||
|
//! Iterators for [IndexMap](super::IndexMap)
|
||||||
|
use super::MapIndex;
|
||||||
|
use std::{marker::PhantomData, ops::Range};
|
||||||
|
|
||||||
|
/// Iterates over the keys of an [IndexMap](super::IndexMap), independently of the map.
|
||||||
|
///
|
||||||
|
/// This is guaranteed to never overrun the length of the map, but is *NOT* guaranteed
|
||||||
|
/// to iterate over all elements of the map if the map is extended during iteration.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct MapIndexIter<K: MapIndex> {
|
||||||
|
range: Range<usize>,
|
||||||
|
_id: PhantomData<K>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: MapIndex> MapIndexIter<K> {
|
||||||
|
/// Creates a new [MapIndexIter] producing the given [MapIndex]
|
||||||
|
pub(super) fn new(range: Range<usize>) -> Self {
|
||||||
|
Self { range, _id: PhantomData }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<ID: MapIndex> Iterator for MapIndexIter<ID> {
|
||||||
|
type Item = ID;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
Some(ID::from_usize(self.range.next()?))
|
||||||
|
}
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
self.range.size_hint()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<ID: MapIndex> DoubleEndedIterator for MapIndexIter<ID> {
|
||||||
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||||||
|
// Safety: see above
|
||||||
|
Some(ID::from_usize(self.range.next_back()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<ID: MapIndex> ExactSizeIterator for MapIndexIter<ID> {}
|
||||||
|
}
|
||||||
319
compiler/cl-structures/src/intern.rs
Normal file
319
compiler/cl-structures/src/intern.rs
Normal file
@@ -0,0 +1,319 @@
|
|||||||
|
//! Interners for [strings](string_interner) and arbitrary [types](typed_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<T: ?Sized + Debug> Debug for Interned<'_, T> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "~")?;
|
||||||
|
self.value.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a, T: ?Sized> Interned<'a, T> {
|
||||||
|
pub(super) fn new(value: &'a T) -> Self {
|
||||||
|
Self { value }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: ?Sized> Deref for Interned<'_, T> {
|
||||||
|
type Target = T;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: ?Sized> Copy for Interned<'_, T> {}
|
||||||
|
impl<T: ?Sized> Clone for Interned<'_, T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: These implementations are subtly incorrect, as they do not line up with `eq`
|
||||||
|
// impl<'a, T: ?Sized + PartialOrd> PartialOrd for Interned<'a, T> {
|
||||||
|
// fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
// match self == other {
|
||||||
|
// true => Some(std::cmp::Ordering::Equal),
|
||||||
|
// false => self.value.partial_cmp(other.value),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// impl<'a, T: ?Sized + Ord> Ord for Interned<'a, T> {
|
||||||
|
// fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
// match self == other {
|
||||||
|
// true => std::cmp::Ordering::Equal,
|
||||||
|
// false => self.value.cmp(other.value),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
impl<T: ?Sized> Eq for Interned<'_, T> {}
|
||||||
|
impl<T: ?Sized> PartialEq for Interned<'_, T> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
std::ptr::eq(self.value, other.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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<T: AsRef<str>> From<T> for Interned<'static, str> {
|
||||||
|
/// Types which implement [`AsRef<str>`] will be stored in the global [StringInterner]
|
||||||
|
fn from(value: T) -> Self {
|
||||||
|
from_str(value.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 typed_interner {
|
||||||
|
//! A [TypedInterner] 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.
|
||||||
|
use super::interned::Interned;
|
||||||
|
use cl_arena::typed_arena::TypedArena;
|
||||||
|
use std::{collections::HashSet, hash::Hash, sync::RwLock};
|
||||||
|
|
||||||
|
/// A [TypedInterner] hands out [Interned] references for arbitrary types.
|
||||||
|
///
|
||||||
|
/// See the [module-level documentation](self) for more information.
|
||||||
|
pub struct TypedInterner<'a, T: Eq + Hash> {
|
||||||
|
arena: TypedArena<'a, T>,
|
||||||
|
keys: RwLock<HashSet<&'a T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Eq + Hash> Default for TypedInterner<'a, T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { arena: Default::default(), keys: Default::default() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Eq + Hash> TypedInterner<'a, T> {
|
||||||
|
/// Creates a new [TypedInterner] backed by the provided [TypedArena]
|
||||||
|
pub fn new(arena: TypedArena<'a, T>) -> 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 = 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](TypedInterner::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 TypedInterner<'a, T> where &'a T: Send {}
|
||||||
|
unsafe impl<T: Eq + Hash + Send + Sync> Sync for TypedInterner<'_, T> {}
|
||||||
|
}
|
||||||
24
compiler/cl-structures/src/lib.rs
Normal file
24
compiler/cl-structures/src/lib.rs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
//! # Universally useful structures
|
||||||
|
//! - [Span](struct@span::Span): Stores a start and end [Loc](struct@span::Loc)
|
||||||
|
//! - [Loc](struct@span::Loc): Stores the index in a stream
|
||||||
|
//! - [TypedInterner][ti] & [StringInterner][si]: Provies stable, unique allocations
|
||||||
|
//! - [Stack](stack::Stack): Contiguous collections with constant capacity
|
||||||
|
//! - [IndexMap][im]: A map from [map indices][mi] to values
|
||||||
|
//!
|
||||||
|
//! [ti]: intern::typed_interner::TypedInterner
|
||||||
|
//! [si]: intern::string_interner::StringInterner
|
||||||
|
//! [im]: index_map::IndexMap
|
||||||
|
//! [mi]: index_map::MapIndex
|
||||||
|
#![warn(clippy::all)]
|
||||||
|
#![feature(dropck_eyepatch, decl_macro)]
|
||||||
|
#![deny(unsafe_op_in_unsafe_fn)]
|
||||||
|
|
||||||
|
pub mod intern;
|
||||||
|
|
||||||
|
pub mod span;
|
||||||
|
|
||||||
|
pub mod tree;
|
||||||
|
|
||||||
|
pub mod stack;
|
||||||
|
|
||||||
|
pub mod index_map;
|
||||||
@@ -8,24 +8,33 @@ pub struct Span {
|
|||||||
pub head: Loc,
|
pub head: Loc,
|
||||||
pub tail: Loc,
|
pub tail: Loc,
|
||||||
}
|
}
|
||||||
pub fn Span(head: Loc, tail: Loc) -> Span {
|
pub const fn Span(head: Loc, tail: Loc) -> Span {
|
||||||
Span { head, tail }
|
Span { head, tail }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Span {
|
||||||
|
pub const fn dummy() -> Self {
|
||||||
|
Span { head: Loc::dummy(), tail: Loc::dummy() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Stores a read-only (line, column) location in a token stream
|
/// Stores a read-only (line, column) location in a token stream
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct Loc {
|
pub struct Loc {
|
||||||
line: u32,
|
line: u32,
|
||||||
col: u32,
|
col: u32,
|
||||||
}
|
}
|
||||||
pub fn Loc(line: u32, col: u32) -> Loc {
|
pub const fn Loc(line: u32, col: u32) -> Loc {
|
||||||
Loc { line, col }
|
Loc { line, col }
|
||||||
}
|
}
|
||||||
impl Loc {
|
impl Loc {
|
||||||
pub fn line(self) -> u32 {
|
pub const fn dummy() -> Self {
|
||||||
|
Loc { line: 0, col: 0 }
|
||||||
|
}
|
||||||
|
pub const fn line(self) -> u32 {
|
||||||
self.line
|
self.line
|
||||||
}
|
}
|
||||||
pub fn col(self) -> u32 {
|
pub const fn col(self) -> u32 {
|
||||||
self.col
|
self.col
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,6 +42,6 @@ impl Loc {
|
|||||||
impl std::fmt::Display for Loc {
|
impl std::fmt::Display for Loc {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let Loc { line, col } = self;
|
let Loc { line, col } = self;
|
||||||
write!(f, "{line}:{col}:")
|
write!(f, "{line}:{col}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,8 +93,8 @@ use std::{
|
|||||||
/// assert_eq!(Some(&10), v.last());
|
/// assert_eq!(Some(&10), v.last());
|
||||||
/// ```
|
/// ```
|
||||||
pub macro stack {
|
pub macro stack {
|
||||||
($count:literal) => {
|
($capacity:literal) => {
|
||||||
Stack::<_, $count>::new()
|
Stack::<_, $capacity>::new()
|
||||||
},
|
},
|
||||||
($value:expr ; $count:literal) => {{
|
($value:expr ; $count:literal) => {{
|
||||||
let mut stack: Stack<_, $count> = Stack::new();
|
let mut stack: Stack<_, $count> = Stack::new();
|
||||||
@@ -103,6 +103,13 @@ pub macro stack {
|
|||||||
}
|
}
|
||||||
stack
|
stack
|
||||||
}},
|
}},
|
||||||
|
($value:expr ; $count:literal ; $capacity:literal) => {{
|
||||||
|
let mut stack: Stack<_, $capacity> = Stack::new();
|
||||||
|
for _ in 0..$count {
|
||||||
|
stack.push($value)
|
||||||
|
}
|
||||||
|
stack
|
||||||
|
}},
|
||||||
($($values:expr),* $(,)?) => {
|
($($values:expr),* $(,)?) => {
|
||||||
Stack::from([$($values),*])
|
Stack::from([$($values),*])
|
||||||
}
|
}
|
||||||
@@ -142,20 +149,14 @@ impl<T, const N: usize> Deref for Stack<T, N> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
// Safety:
|
self.as_slice()
|
||||||
// - We have ensured all elements from 0 to len have been initialized
|
|
||||||
// - self.elem[0] came from a reference, and so is aligned to T
|
|
||||||
// unsafe { &*(&self.buf[0..self.len] as *const [_] as *const [T]) }
|
|
||||||
unsafe { slice::from_raw_parts(self.buf.as_ptr().cast(), self.len) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> DerefMut for Stack<T, N> {
|
impl<T, const N: usize> DerefMut for Stack<T, N> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
// Safety:
|
self.as_mut_slice()
|
||||||
// - See Deref
|
|
||||||
unsafe { slice::from_raw_parts_mut(self.buf.as_mut_ptr().cast(), self.len) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,8 +164,10 @@ impl<T, const N: usize> DerefMut for Stack<T, N> {
|
|||||||
unsafe impl<#[may_dangle] T, const N: usize> Drop for Stack<T, N> {
|
unsafe impl<#[may_dangle] T, const N: usize> Drop for Stack<T, N> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// Safety: We have ensured that all elements in the list are
|
// Safety: Elements in [0..self.len] are initialized
|
||||||
unsafe { core::ptr::drop_in_place(self.as_mut_slice()) };
|
if std::mem::needs_drop::<T>() {
|
||||||
|
unsafe { core::ptr::drop_in_place(self.as_mut_slice()) };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,18 +272,24 @@ impl<T, const N: usize> Stack<T, N> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an unsafe mutable pointer to the stack's buffer
|
/// Returns an unsafe mutable pointer to the stack's buffer
|
||||||
pub fn as_mut_ptr(&mut self) -> *mut T {
|
pub const fn as_mut_ptr(&mut self) -> *mut T {
|
||||||
self.buf.as_mut_ptr().cast()
|
self.buf.as_mut_ptr().cast()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extracts a slice containing the entire vector
|
/// Extracts a slice containing the entire vector
|
||||||
pub fn as_slice(&self) -> &[T] {
|
pub const fn as_slice(&self) -> &[T] {
|
||||||
self
|
// Safety:
|
||||||
|
// - We have ensured all elements from 0 to len have been initialized
|
||||||
|
// - self.elem[0] came from a reference, and so is aligned to T
|
||||||
|
// unsafe { &*(&self.buf[0..self.len] as *const [_] as *const [T]) }
|
||||||
|
unsafe { slice::from_raw_parts(self.buf.as_ptr().cast(), self.len) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extracts a mutable slice containing the entire vector
|
/// Extracts a mutable slice containing the entire vector
|
||||||
pub fn as_mut_slice(&mut self) -> &mut [T] {
|
pub const fn as_mut_slice(&mut self) -> &mut [T] {
|
||||||
self
|
// Safety:
|
||||||
|
// - See Stack::as_slice
|
||||||
|
unsafe { slice::from_raw_parts_mut(self.buf.as_mut_ptr().cast(), self.len) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the total number of elements the stack can hold
|
/// Returns the total number of elements the stack can hold
|
||||||
@@ -353,7 +362,7 @@ impl<T, const N: usize> Stack<T, N> {
|
|||||||
/// v.push(3);
|
/// v.push(3);
|
||||||
/// assert_eq!(&[0, 1, 2, 3], v.as_slice());
|
/// assert_eq!(&[0, 1, 2, 3], v.as_slice());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn push(&mut self, value: T) {
|
pub const fn push(&mut self, value: T) {
|
||||||
if self.len >= N {
|
if self.len >= N {
|
||||||
panic!("Attempted to push into full stack")
|
panic!("Attempted to push into full stack")
|
||||||
}
|
}
|
||||||
@@ -364,7 +373,7 @@ impl<T, const N: usize> Stack<T, N> {
|
|||||||
/// Push a new element onto the end of the stack
|
/// Push a new element onto the end of the stack
|
||||||
///
|
///
|
||||||
/// Returns [`Err(value)`](Result::Err) if the new length would exceed capacity
|
/// Returns [`Err(value)`](Result::Err) if the new length would exceed capacity
|
||||||
pub fn try_push(&mut self, value: T) -> Result<(), T> {
|
pub const fn try_push(&mut self, value: T) -> Result<(), T> {
|
||||||
if self.len >= N {
|
if self.len >= N {
|
||||||
return Err(value);
|
return Err(value);
|
||||||
}
|
}
|
||||||
@@ -379,8 +388,11 @@ impl<T, const N: usize> Stack<T, N> {
|
|||||||
///
|
///
|
||||||
/// len after push must not exceed capacity N
|
/// len after push must not exceed capacity N
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn push_unchecked(&mut self, value: T) {
|
const unsafe fn push_unchecked(&mut self, value: T) {
|
||||||
unsafe { ptr::write(self.as_mut_ptr().add(self.len), value) }
|
unsafe {
|
||||||
|
// self.buf.get_unchecked_mut(self.len).write(value); // TODO: This is non-const
|
||||||
|
ptr::write(self.as_mut_ptr().add(self.len), value)
|
||||||
|
}
|
||||||
self.len += 1; // post inc
|
self.len += 1; // post inc
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,13 +412,14 @@ impl<T, const N: usize> Stack<T, N> {
|
|||||||
/// assert_eq!(Some(0), v.pop());
|
/// assert_eq!(Some(0), v.pop());
|
||||||
/// assert_eq!(None, v.pop());
|
/// assert_eq!(None, v.pop());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn pop(&mut self) -> Option<T> {
|
pub const fn pop(&mut self) -> Option<T> {
|
||||||
if self.len == 0 {
|
if self.len == 0 {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
self.len -= 1;
|
self.len -= 1;
|
||||||
// Safety: MaybeUninit<T> implies ManuallyDrop<T>,
|
// Safety: MaybeUninit<T> implies ManuallyDrop<T>,
|
||||||
// therefore should not get dropped twice
|
// therefore should not get dropped twice
|
||||||
|
// Some(unsafe { self.buf.get_unchecked_mut(self.len).assume_init_read() })
|
||||||
Some(unsafe { ptr::read(self.as_ptr().add(self.len).cast()) })
|
Some(unsafe { ptr::read(self.as_ptr().add(self.len).cast()) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -505,7 +518,7 @@ impl<T, const N: usize> Stack<T, N> {
|
|||||||
///
|
///
|
||||||
/// assert_eq!(Ok(()), v.try_insert(0, 0));
|
/// assert_eq!(Ok(()), v.try_insert(0, 0));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn try_insert(&mut self, index: usize, data: T) -> Result<(), (T, InsertFailed<N>)> {
|
pub const fn try_insert(&mut self, index: usize, data: T) -> Result<(), (T, InsertFailed<N>)> {
|
||||||
if index > self.len {
|
if index > self.len {
|
||||||
return Err((data, InsertFailed::Bounds(index)));
|
return Err((data, InsertFailed::Bounds(index)));
|
||||||
}
|
}
|
||||||
@@ -521,7 +534,7 @@ impl<T, const N: usize> Stack<T, N> {
|
|||||||
/// - index must be less than self.len
|
/// - index must be less than self.len
|
||||||
/// - length after insertion must be <= N
|
/// - length after insertion must be <= N
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn insert_unchecked(&mut self, index: usize, data: T) {
|
const unsafe fn insert_unchecked(&mut self, index: usize, data: T) {
|
||||||
let base = self.as_mut_ptr();
|
let base = self.as_mut_ptr();
|
||||||
|
|
||||||
unsafe { ptr::copy(base.add(index), base.add(index + 1), self.len - index) }
|
unsafe { ptr::copy(base.add(index), base.add(index + 1), self.len - index) }
|
||||||
@@ -545,7 +558,9 @@ impl<T, const N: usize> Stack<T, N> {
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
// Hopefully copy elision takes care of this lmao
|
// Hopefully copy elision takes care of this lmao
|
||||||
drop(std::mem::take(self))
|
while !self.is_empty() {
|
||||||
|
drop(self.pop());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of elements in the stack
|
/// Returns the number of elements in the stack
|
||||||
@@ -555,7 +570,7 @@ impl<T, const N: usize> Stack<T, N> {
|
|||||||
///
|
///
|
||||||
/// assert_eq!(5, v.len());
|
/// assert_eq!(5, v.len());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn len(&self) -> usize {
|
pub const fn len(&self) -> usize {
|
||||||
self.len
|
self.len
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -570,7 +585,7 @@ impl<T, const N: usize> Stack<T, N> {
|
|||||||
/// assert!(v.is_full());
|
/// assert!(v.is_full());
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_full(&self) -> bool {
|
pub const fn is_full(&self) -> bool {
|
||||||
self.len >= N
|
self.len >= N
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -585,7 +600,7 @@ impl<T, const N: usize> Stack<T, N> {
|
|||||||
/// assert!(v.is_empty());
|
/// assert!(v.is_empty());
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub const fn is_empty(&self) -> bool {
|
||||||
self.len == 0
|
self.len == 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -617,13 +632,13 @@ mod tests {
|
|||||||
assert_eq!(std::mem::size_of::<usize>(), std::mem::size_of_val(&v))
|
assert_eq!(std::mem::size_of::<usize>(), std::mem::size_of_val(&v))
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(debug_assertions, ignore = "calls ().drop() usize::MAX times")]
|
|
||||||
fn from_usize_max_zst_array() {
|
fn from_usize_max_zst_array() {
|
||||||
let mut v = Stack::from([(); usize::MAX]);
|
let mut v = Stack::from([(); usize::MAX]);
|
||||||
assert_eq!(v.len(), usize::MAX);
|
assert_eq!(v.len(), usize::MAX);
|
||||||
v.pop();
|
v.pop();
|
||||||
assert_eq!(v.len(), usize::MAX - 1);
|
assert_eq!(v.len(), usize::MAX - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn new() {
|
fn new() {
|
||||||
let v: Stack<(), 255> = Stack::new();
|
let v: Stack<(), 255> = Stack::new();
|
||||||
@@ -744,4 +759,19 @@ mod tests {
|
|||||||
]);
|
]);
|
||||||
std::mem::drop(std::hint::black_box(v));
|
std::mem::drop(std::hint::black_box(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn drop_zst() {
|
||||||
|
struct Droppable;
|
||||||
|
impl Drop for Droppable {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
use std::sync::atomic::{AtomicU32, Ordering};
|
||||||
|
static V: AtomicU32 = AtomicU32::new(1);
|
||||||
|
eprintln!("{}", V.fetch_add(1, Ordering::Relaxed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let v = Stack::from([const { Droppable }; 10]);
|
||||||
|
std::mem::drop(v);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -10,4 +10,4 @@ pub mod token_type;
|
|||||||
|
|
||||||
pub use token::Token;
|
pub use token::Token;
|
||||||
pub use token_data::TokenData;
|
pub use token_data::TokenData;
|
||||||
pub use token_type::{Punct, TokenKind};
|
pub use token_type::TokenKind;
|
||||||
@@ -13,38 +13,35 @@ pub enum TokenKind {
|
|||||||
/// A non-keyword identifier
|
/// A non-keyword identifier
|
||||||
Identifier,
|
Identifier,
|
||||||
// A keyword
|
// A keyword
|
||||||
Break,
|
As, // as
|
||||||
Cl,
|
Break, // "break"
|
||||||
Const,
|
Cl, // "cl"
|
||||||
Continue,
|
Const, // "const"
|
||||||
Else,
|
Continue, // "continue"
|
||||||
Enum,
|
Else, // "else"
|
||||||
False,
|
Enum, // "enum"
|
||||||
For,
|
False, // "false"
|
||||||
Fn,
|
Fn, // "fn"
|
||||||
If,
|
For, // "for"
|
||||||
Impl,
|
If, // "if"
|
||||||
In,
|
Impl, // "impl"
|
||||||
Let,
|
In, // "in"
|
||||||
Mod,
|
Let, // "let"
|
||||||
Mut,
|
Loop, // "loop"
|
||||||
Pub,
|
Match, // "match"
|
||||||
Return,
|
Mod, // "mod"
|
||||||
SelfKw,
|
Mut, // "mut"
|
||||||
SelfTy,
|
Pub, // "pub"
|
||||||
Static,
|
Return, // "return"
|
||||||
Struct,
|
SelfTy, // "Self"
|
||||||
Super,
|
Static, // "static"
|
||||||
True,
|
Struct, // "struct"
|
||||||
Type,
|
Super, // "super"
|
||||||
While,
|
True, // "true"
|
||||||
/// Delimiter or punctuation
|
Type, // "type"
|
||||||
Punct(Punct),
|
Use, // "use"
|
||||||
}
|
While, // "while"
|
||||||
|
// Delimiter or punctuation
|
||||||
/// An operator character (delimiter, punctuation)
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub enum Punct {
|
|
||||||
LCurly, // {
|
LCurly, // {
|
||||||
RCurly, // }
|
RCurly, // }
|
||||||
LBrack, // [
|
LBrack, // [
|
||||||
@@ -109,6 +106,7 @@ impl Display for TokenKind {
|
|||||||
TokenKind::Literal => "literal".fmt(f),
|
TokenKind::Literal => "literal".fmt(f),
|
||||||
TokenKind::Identifier => "identifier".fmt(f),
|
TokenKind::Identifier => "identifier".fmt(f),
|
||||||
|
|
||||||
|
TokenKind::As => "as".fmt(f),
|
||||||
TokenKind::Break => "break".fmt(f),
|
TokenKind::Break => "break".fmt(f),
|
||||||
TokenKind::Cl => "cl".fmt(f),
|
TokenKind::Cl => "cl".fmt(f),
|
||||||
TokenKind::Const => "const".fmt(f),
|
TokenKind::Const => "const".fmt(f),
|
||||||
@@ -116,26 +114,81 @@ impl Display for TokenKind {
|
|||||||
TokenKind::Else => "else".fmt(f),
|
TokenKind::Else => "else".fmt(f),
|
||||||
TokenKind::Enum => "enum".fmt(f),
|
TokenKind::Enum => "enum".fmt(f),
|
||||||
TokenKind::False => "false".fmt(f),
|
TokenKind::False => "false".fmt(f),
|
||||||
TokenKind::For => "for".fmt(f),
|
|
||||||
TokenKind::Fn => "fn".fmt(f),
|
TokenKind::Fn => "fn".fmt(f),
|
||||||
|
TokenKind::For => "for".fmt(f),
|
||||||
TokenKind::If => "if".fmt(f),
|
TokenKind::If => "if".fmt(f),
|
||||||
TokenKind::Impl => "impl".fmt(f),
|
TokenKind::Impl => "impl".fmt(f),
|
||||||
TokenKind::In => "in".fmt(f),
|
TokenKind::In => "in".fmt(f),
|
||||||
TokenKind::Let => "let".fmt(f),
|
TokenKind::Let => "let".fmt(f),
|
||||||
|
TokenKind::Loop => "loop".fmt(f),
|
||||||
|
TokenKind::Match => "match".fmt(f),
|
||||||
TokenKind::Mod => "mod".fmt(f),
|
TokenKind::Mod => "mod".fmt(f),
|
||||||
TokenKind::Mut => "mut".fmt(f),
|
TokenKind::Mut => "mut".fmt(f),
|
||||||
TokenKind::Pub => "pub".fmt(f),
|
TokenKind::Pub => "pub".fmt(f),
|
||||||
TokenKind::Return => "return".fmt(f),
|
TokenKind::Return => "return".fmt(f),
|
||||||
TokenKind::SelfKw => "self".fmt(f),
|
|
||||||
TokenKind::SelfTy => "Self".fmt(f),
|
TokenKind::SelfTy => "Self".fmt(f),
|
||||||
TokenKind::Static => "static".fmt(f),
|
TokenKind::Static => "static".fmt(f),
|
||||||
TokenKind::Struct => "struct".fmt(f),
|
TokenKind::Struct => "struct".fmt(f),
|
||||||
TokenKind::Super => "super".fmt(f),
|
TokenKind::Super => "super".fmt(f),
|
||||||
TokenKind::True => "true".fmt(f),
|
TokenKind::True => "true".fmt(f),
|
||||||
TokenKind::Type => "type".fmt(f),
|
TokenKind::Type => "type".fmt(f),
|
||||||
|
TokenKind::Use => "use".fmt(f),
|
||||||
TokenKind::While => "while".fmt(f),
|
TokenKind::While => "while".fmt(f),
|
||||||
|
|
||||||
TokenKind::Punct(op) => op.fmt(f),
|
TokenKind::LCurly => "{".fmt(f),
|
||||||
|
TokenKind::RCurly => "}".fmt(f),
|
||||||
|
TokenKind::LBrack => "[".fmt(f),
|
||||||
|
TokenKind::RBrack => "]".fmt(f),
|
||||||
|
TokenKind::LParen => "(".fmt(f),
|
||||||
|
TokenKind::RParen => ")".fmt(f),
|
||||||
|
TokenKind::Amp => "&".fmt(f),
|
||||||
|
TokenKind::AmpAmp => "&&".fmt(f),
|
||||||
|
TokenKind::AmpEq => "&=".fmt(f),
|
||||||
|
TokenKind::Arrow => "->".fmt(f),
|
||||||
|
TokenKind::At => "@".fmt(f),
|
||||||
|
TokenKind::Backslash => "\\".fmt(f),
|
||||||
|
TokenKind::Bang => "!".fmt(f),
|
||||||
|
TokenKind::BangBang => "!!".fmt(f),
|
||||||
|
TokenKind::BangEq => "!=".fmt(f),
|
||||||
|
TokenKind::Bar => "|".fmt(f),
|
||||||
|
TokenKind::BarBar => "||".fmt(f),
|
||||||
|
TokenKind::BarEq => "|=".fmt(f),
|
||||||
|
TokenKind::Colon => ":".fmt(f),
|
||||||
|
TokenKind::ColonColon => "::".fmt(f),
|
||||||
|
TokenKind::Comma => ",".fmt(f),
|
||||||
|
TokenKind::Dot => ".".fmt(f),
|
||||||
|
TokenKind::DotDot => "..".fmt(f),
|
||||||
|
TokenKind::DotDotEq => "..=".fmt(f),
|
||||||
|
TokenKind::Eq => "=".fmt(f),
|
||||||
|
TokenKind::EqEq => "==".fmt(f),
|
||||||
|
TokenKind::FatArrow => "=>".fmt(f),
|
||||||
|
TokenKind::Grave => "`".fmt(f),
|
||||||
|
TokenKind::Gt => ">".fmt(f),
|
||||||
|
TokenKind::GtEq => ">=".fmt(f),
|
||||||
|
TokenKind::GtGt => ">>".fmt(f),
|
||||||
|
TokenKind::GtGtEq => ">>=".fmt(f),
|
||||||
|
TokenKind::Hash => "#".fmt(f),
|
||||||
|
TokenKind::HashBang => "#!".fmt(f),
|
||||||
|
TokenKind::Lt => "<".fmt(f),
|
||||||
|
TokenKind::LtEq => "<=".fmt(f),
|
||||||
|
TokenKind::LtLt => "<<".fmt(f),
|
||||||
|
TokenKind::LtLtEq => "<<=".fmt(f),
|
||||||
|
TokenKind::Minus => "-".fmt(f),
|
||||||
|
TokenKind::MinusEq => "-=".fmt(f),
|
||||||
|
TokenKind::Plus => "+".fmt(f),
|
||||||
|
TokenKind::PlusEq => "+=".fmt(f),
|
||||||
|
TokenKind::Question => "?".fmt(f),
|
||||||
|
TokenKind::Rem => "%".fmt(f),
|
||||||
|
TokenKind::RemEq => "%=".fmt(f),
|
||||||
|
TokenKind::Semi => ";".fmt(f),
|
||||||
|
TokenKind::Slash => "/".fmt(f),
|
||||||
|
TokenKind::SlashEq => "/=".fmt(f),
|
||||||
|
TokenKind::Star => "*".fmt(f),
|
||||||
|
TokenKind::StarEq => "*=".fmt(f),
|
||||||
|
TokenKind::Tilde => "~".fmt(f),
|
||||||
|
TokenKind::Xor => "^".fmt(f),
|
||||||
|
TokenKind::XorEq => "^=".fmt(f),
|
||||||
|
TokenKind::XorXor => "^^".fmt(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,6 +198,7 @@ impl FromStr for TokenKind {
|
|||||||
/// Parses a string s to return a Keyword
|
/// Parses a string s to return a Keyword
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
Ok(match s {
|
Ok(match s {
|
||||||
|
"as" => Self::As,
|
||||||
"break" => Self::Break,
|
"break" => Self::Break,
|
||||||
"cl" => Self::Cl,
|
"cl" => Self::Cl,
|
||||||
"const" => Self::Const,
|
"const" => Self::Const,
|
||||||
@@ -152,86 +206,27 @@ impl FromStr for TokenKind {
|
|||||||
"else" => Self::Else,
|
"else" => Self::Else,
|
||||||
"enum" => Self::Enum,
|
"enum" => Self::Enum,
|
||||||
"false" => Self::False,
|
"false" => Self::False,
|
||||||
"for" => Self::For,
|
|
||||||
"fn" => Self::Fn,
|
"fn" => Self::Fn,
|
||||||
|
"for" => Self::For,
|
||||||
"if" => Self::If,
|
"if" => Self::If,
|
||||||
"impl" => Self::Impl,
|
"impl" => Self::Impl,
|
||||||
"in" => Self::In,
|
"in" => Self::In,
|
||||||
"let" => Self::Let,
|
"let" => Self::Let,
|
||||||
|
"loop" => Self::Loop,
|
||||||
|
"match" => Self::Match,
|
||||||
"mod" => Self::Mod,
|
"mod" => Self::Mod,
|
||||||
"mut" => Self::Mut,
|
"mut" => Self::Mut,
|
||||||
"pub" => Self::Pub,
|
"pub" => Self::Pub,
|
||||||
"return" => Self::Return,
|
"return" => Self::Return,
|
||||||
"self" => Self::SelfKw,
|
|
||||||
"Self" => Self::SelfTy,
|
"Self" => Self::SelfTy,
|
||||||
"static" => Self::Static,
|
"static" => Self::Static,
|
||||||
"struct" => Self::Struct,
|
"struct" => Self::Struct,
|
||||||
"super" => Self::Super,
|
"super" => Self::Super,
|
||||||
"true" => Self::True,
|
"true" => Self::True,
|
||||||
"type" => Self::Type,
|
"type" => Self::Type,
|
||||||
|
"use" => Self::Use,
|
||||||
"while" => Self::While,
|
"while" => Self::While,
|
||||||
_ => Err(())?,
|
_ => Err(())?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Punct {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Punct::LCurly => "{".fmt(f),
|
|
||||||
Punct::RCurly => "}".fmt(f),
|
|
||||||
Punct::LBrack => "[".fmt(f),
|
|
||||||
Punct::RBrack => "]".fmt(f),
|
|
||||||
Punct::LParen => "(".fmt(f),
|
|
||||||
Punct::RParen => ")".fmt(f),
|
|
||||||
Punct::Amp => "&".fmt(f),
|
|
||||||
Punct::AmpAmp => "&&".fmt(f),
|
|
||||||
Punct::AmpEq => "&=".fmt(f),
|
|
||||||
Punct::Arrow => "->".fmt(f),
|
|
||||||
Punct::At => "@".fmt(f),
|
|
||||||
Punct::Backslash => "\\".fmt(f),
|
|
||||||
Punct::Bang => "!".fmt(f),
|
|
||||||
Punct::BangBang => "!!".fmt(f),
|
|
||||||
Punct::BangEq => "!=".fmt(f),
|
|
||||||
Punct::Bar => "|".fmt(f),
|
|
||||||
Punct::BarBar => "||".fmt(f),
|
|
||||||
Punct::BarEq => "|=".fmt(f),
|
|
||||||
Punct::Colon => ":".fmt(f),
|
|
||||||
Punct::ColonColon => "::".fmt(f),
|
|
||||||
Punct::Comma => ",".fmt(f),
|
|
||||||
Punct::Dot => ".".fmt(f),
|
|
||||||
Punct::DotDot => "..".fmt(f),
|
|
||||||
Punct::DotDotEq => "..=".fmt(f),
|
|
||||||
Punct::Eq => "=".fmt(f),
|
|
||||||
Punct::EqEq => "==".fmt(f),
|
|
||||||
Punct::FatArrow => "=>".fmt(f),
|
|
||||||
Punct::Grave => "`".fmt(f),
|
|
||||||
Punct::Gt => ">".fmt(f),
|
|
||||||
Punct::GtEq => ">=".fmt(f),
|
|
||||||
Punct::GtGt => ">>".fmt(f),
|
|
||||||
Punct::GtGtEq => ">>=".fmt(f),
|
|
||||||
Punct::Hash => "#".fmt(f),
|
|
||||||
Punct::HashBang => "#!".fmt(f),
|
|
||||||
Punct::Lt => "<".fmt(f),
|
|
||||||
Punct::LtEq => "<=".fmt(f),
|
|
||||||
Punct::LtLt => "<<".fmt(f),
|
|
||||||
Punct::LtLtEq => "<<=".fmt(f),
|
|
||||||
Punct::Minus => "-".fmt(f),
|
|
||||||
Punct::MinusEq => "-=".fmt(f),
|
|
||||||
Punct::Plus => "+".fmt(f),
|
|
||||||
Punct::PlusEq => "+=".fmt(f),
|
|
||||||
Punct::Question => "?".fmt(f),
|
|
||||||
Punct::Rem => "%".fmt(f),
|
|
||||||
Punct::RemEq => "%=".fmt(f),
|
|
||||||
Punct::Semi => ";".fmt(f),
|
|
||||||
Punct::Slash => "/".fmt(f),
|
|
||||||
Punct::SlashEq => "/=".fmt(f),
|
|
||||||
Punct::Star => "*".fmt(f),
|
|
||||||
Punct::StarEq => "*=".fmt(f),
|
|
||||||
Punct::Tilde => "~".fmt(f),
|
|
||||||
Punct::Xor => "^".fmt(f),
|
|
||||||
Punct::XorEq => "^=".fmt(f),
|
|
||||||
Punct::XorXor => "^^".fmt(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,3 +10,8 @@ publish.workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
cl-ast = { path = "../cl-ast" }
|
cl-ast = { path = "../cl-ast" }
|
||||||
cl-structures = { path = "../cl-structures" }
|
cl-structures = { path = "../cl-structures" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
repline = { version = "*", registry = "soft-fish" }
|
||||||
|
cl-lexer = { path = "../cl-lexer" }
|
||||||
|
cl-parser = { path = "../cl-parser" }
|
||||||
462
compiler/cl-typeck/examples/typeck.rs
Normal file
462
compiler/cl-typeck/examples/typeck.rs
Normal file
@@ -0,0 +1,462 @@
|
|||||||
|
use cl_typeck::{
|
||||||
|
entry::Entry,
|
||||||
|
stage::{
|
||||||
|
infer::{engine::InferenceEngine, error::InferenceError, inference::Inference},
|
||||||
|
*,
|
||||||
|
},
|
||||||
|
table::Table,
|
||||||
|
type_expression::TypeExpression,
|
||||||
|
};
|
||||||
|
|
||||||
|
use cl_ast::{
|
||||||
|
Expr, Path, Stmt, Ty,
|
||||||
|
ast_visitor::{Fold, Visit},
|
||||||
|
desugar::*,
|
||||||
|
};
|
||||||
|
use cl_lexer::Lexer;
|
||||||
|
use cl_parser::{Parser, inliner::ModuleInliner};
|
||||||
|
use cl_structures::intern::string_interner::StringInterner;
|
||||||
|
use repline::{error::Error as RlError, prebaked::*};
|
||||||
|
use std::{
|
||||||
|
error::Error,
|
||||||
|
path::{self, PathBuf},
|
||||||
|
sync::LazyLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Path to display in standard library errors
|
||||||
|
const STDLIB_DISPLAY_PATH: &str = "stdlib/lib.cl";
|
||||||
|
// Statically included standard library
|
||||||
|
const PREAMBLE: &str = r"
|
||||||
|
pub mod std;
|
||||||
|
pub use std::preamble::*;
|
||||||
|
";
|
||||||
|
|
||||||
|
// Colors
|
||||||
|
const C_MAIN: &str = C_LISTING;
|
||||||
|
const C_RESV: &str = "\x1b[35m";
|
||||||
|
const C_CODE: &str = "\x1b[36m";
|
||||||
|
const C_BYID: &str = "\x1b[95m";
|
||||||
|
const C_ERROR: &str = "\x1b[31m";
|
||||||
|
const C_LISTING: &str = "\x1b[38;5;117m";
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut prj = Table::default();
|
||||||
|
|
||||||
|
let mut parser = Parser::new("PREAMBLE", Lexer::new(PREAMBLE));
|
||||||
|
let code = match parser.parse() {
|
||||||
|
Ok(code) => code,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{STDLIB_DISPLAY_PATH}:{e}");
|
||||||
|
Err(e)?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// This code is special - it gets loaded from a hard-coded project directory (for now)
|
||||||
|
let code = inline_modules(code, concat!(env!("CARGO_MANIFEST_DIR"), "/../../stdlib"));
|
||||||
|
let code = cl_ast::desugar::WhileElseDesugar.fold_file(code);
|
||||||
|
Populator::new(&mut prj).visit_file(interned(code));
|
||||||
|
|
||||||
|
for arg in std::env::args().skip(1) {
|
||||||
|
import_file(&mut prj, arg)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_all(&mut prj)?;
|
||||||
|
|
||||||
|
main_menu(&mut prj)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main_menu(prj: &mut Table) -> Result<(), RlError> {
|
||||||
|
banner();
|
||||||
|
read_and(C_MAIN, "mu>", "? >", |line| {
|
||||||
|
for line in line.trim().split_ascii_whitespace() {
|
||||||
|
match line {
|
||||||
|
"c" | "code" => enter_code(prj)?,
|
||||||
|
"clear" => clear()?,
|
||||||
|
"dump" => dump(prj)?,
|
||||||
|
"d" | "desugar" => live_desugar()?,
|
||||||
|
"e" | "exit" => return Ok(Response::Break),
|
||||||
|
"f" | "file" => import_files(prj)?,
|
||||||
|
"i" | "id" => get_by_id(prj)?,
|
||||||
|
"l" | "list" => list_types(prj),
|
||||||
|
"q" | "query" => query_type_expression(prj)?,
|
||||||
|
"r" | "resolve" => resolve_all(prj)?,
|
||||||
|
"s" | "strings" => print_strings(),
|
||||||
|
"a" | "all" => infer_all(prj)?,
|
||||||
|
"t" | "test" => infer_expression(prj)?,
|
||||||
|
"h" | "help" | "" => {
|
||||||
|
println!(
|
||||||
|
"Valid commands are:
|
||||||
|
clear : Clear the screen
|
||||||
|
code (c): Enter code to type-check
|
||||||
|
desugar (d): WIP: Test the experimental desugaring passes
|
||||||
|
file (f): Load files from disk
|
||||||
|
id (i): Get a type by its type ID
|
||||||
|
list (l): List all known types
|
||||||
|
query (q): Query the type system
|
||||||
|
resolve (r): Perform type resolution
|
||||||
|
help (h): Print this list
|
||||||
|
exit (e): Exit the program"
|
||||||
|
);
|
||||||
|
return Ok(Response::Deny);
|
||||||
|
}
|
||||||
|
_ => Err(r#"Invalid command. Type "help" to see the list of valid commands."#)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Response::Accept)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_code(prj: &mut Table) -> Result<(), RlError> {
|
||||||
|
read_and(C_CODE, "cl>", "? >", |line| {
|
||||||
|
if line.trim().is_empty() {
|
||||||
|
return Ok(Response::Break);
|
||||||
|
}
|
||||||
|
let code = Parser::new("", Lexer::new(line)).parse()?;
|
||||||
|
let code = inline_modules(code, "");
|
||||||
|
let code = WhileElseDesugar.fold_file(code);
|
||||||
|
|
||||||
|
Populator::new(prj).visit_file(interned(code));
|
||||||
|
Ok(Response::Accept)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn live_desugar() -> Result<(), RlError> {
|
||||||
|
read_and(C_RESV, "se>", "? >", |line| {
|
||||||
|
let code = Parser::new("", Lexer::new(line)).parse::<Stmt>()?;
|
||||||
|
println!("Raw, as parsed:\n{C_LISTING}{code}\x1b[0m");
|
||||||
|
|
||||||
|
let code = ConstantFolder.fold_stmt(code);
|
||||||
|
println!("ConstantFolder\n{C_LISTING}{code}\x1b[0m");
|
||||||
|
|
||||||
|
let code = SquashGroups.fold_stmt(code);
|
||||||
|
println!("SquashGroups\n{C_LISTING}{code}\x1b[0m");
|
||||||
|
|
||||||
|
let code = WhileElseDesugar.fold_stmt(code);
|
||||||
|
println!("WhileElseDesugar\n{C_LISTING}{code}\x1b[0m");
|
||||||
|
|
||||||
|
let code = NormalizePaths::new().fold_stmt(code);
|
||||||
|
println!("NormalizePaths\n{C_LISTING}{code}\x1b[0m");
|
||||||
|
|
||||||
|
Ok(Response::Accept)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_strings() {
|
||||||
|
println!("{}", StringInterner::global());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_type_expression(prj: &mut Table) -> Result<(), RlError> {
|
||||||
|
read_and(C_RESV, "ty>", "? >", |line| {
|
||||||
|
if line.trim().is_empty() {
|
||||||
|
return Ok(Response::Break);
|
||||||
|
}
|
||||||
|
// A query is comprised of a Ty and a relative Path
|
||||||
|
let mut p = Parser::new("", Lexer::new(line));
|
||||||
|
let ty: Ty = p.parse()?;
|
||||||
|
let path: Path = p
|
||||||
|
.parse()
|
||||||
|
.map(|p| Path { absolute: false, ..p })
|
||||||
|
.unwrap_or_default();
|
||||||
|
let id = ty.evaluate(prj, prj.root())?;
|
||||||
|
let id = path.evaluate(prj, id)?;
|
||||||
|
pretty_handle(id.to_entry(prj))?;
|
||||||
|
Ok(Response::Accept)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn infer_expression(prj: &mut Table) -> Result<(), RlError> {
|
||||||
|
read_and(C_RESV, "ex>", "!?>", |line| {
|
||||||
|
if line.trim().is_empty() {
|
||||||
|
return Ok(Response::Break);
|
||||||
|
}
|
||||||
|
let mut p = Parser::new("", Lexer::new(line));
|
||||||
|
let e: Expr = p.parse()?;
|
||||||
|
let mut inf = InferenceEngine::new(prj, prj.root());
|
||||||
|
let ty = match exp_terned(e).infer(&mut inf) {
|
||||||
|
Ok(ty) => ty,
|
||||||
|
Err(e) => match e {
|
||||||
|
InferenceError::Mismatch(a, b) => {
|
||||||
|
eprintln!("Mismatched types: {}, {}", prj.entry(a), prj.entry(b));
|
||||||
|
return Ok(Response::Deny);
|
||||||
|
}
|
||||||
|
InferenceError::Recursive(a, b) => {
|
||||||
|
eprintln!("Recursive types: {}, {}", prj.entry(a), prj.entry(b));
|
||||||
|
return Ok(Response::Deny);
|
||||||
|
}
|
||||||
|
e => Err(e)?,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
eprintln!("--> {}", prj.entry(ty));
|
||||||
|
Ok(Response::Accept)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_by_id(prj: &mut Table) -> Result<(), RlError> {
|
||||||
|
use cl_parser::parser::Parse;
|
||||||
|
use cl_structures::index_map::MapIndex;
|
||||||
|
use cl_typeck::handle::Handle;
|
||||||
|
read_and(C_BYID, "id>", "? >", |line| {
|
||||||
|
if line.trim().is_empty() {
|
||||||
|
return Ok(Response::Break);
|
||||||
|
}
|
||||||
|
let mut parser = Parser::new("", Lexer::new(line));
|
||||||
|
let def_id = match Parse::parse(&mut parser)? {
|
||||||
|
cl_ast::Literal::Int(int) => int as _,
|
||||||
|
other => Err(format!("Expected integer, got {other}"))?,
|
||||||
|
};
|
||||||
|
let mut path = parser.parse::<cl_ast::Path>().unwrap_or_default();
|
||||||
|
path.absolute = false;
|
||||||
|
|
||||||
|
let handle = Handle::from_usize(def_id).to_entry(prj);
|
||||||
|
|
||||||
|
print!(" > {{{C_LISTING}{handle}\x1b[0m}}");
|
||||||
|
if !path.parts.is_empty() {
|
||||||
|
print!("::{path}")
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
|
||||||
|
let Some(entry) = handle.nav(&path.parts) else {
|
||||||
|
Err("No results.")?
|
||||||
|
};
|
||||||
|
|
||||||
|
pretty_handle(entry)?;
|
||||||
|
|
||||||
|
Ok(Response::Accept)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_all(table: &mut Table) -> Result<(), Box<dyn Error>> {
|
||||||
|
for (id, error) in import(table) {
|
||||||
|
eprintln!("{error} in {} ({id})", id.to_entry(table))
|
||||||
|
}
|
||||||
|
for handle in table.handle_iter() {
|
||||||
|
if let Err(error) = handle.to_entry_mut(table).categorize() {
|
||||||
|
eprintln!("{error}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for handle in implement(table) {
|
||||||
|
eprintln!("Unable to reparent {} ({handle})", handle.to_entry(table))
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("...Resolved!");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn infer_all(table: &mut Table) -> Result<(), Box<dyn Error>> {
|
||||||
|
for (id, error) in InferenceEngine::new(table, table.root()).infer_all() {
|
||||||
|
match error {
|
||||||
|
InferenceError::Mismatch(a, b) => {
|
||||||
|
eprint!("Mismatched types: {}, {}", table.entry(a), table.entry(b));
|
||||||
|
}
|
||||||
|
InferenceError::Recursive(a, b) => {
|
||||||
|
eprint!("Recursive types: {}, {}", table.entry(a), table.entry(b));
|
||||||
|
}
|
||||||
|
e => eprint!("{e}"),
|
||||||
|
}
|
||||||
|
eprintln!(" in {id}\n({})\n", id.to_entry(table).source().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("...Inferred!");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_types(table: &mut Table) {
|
||||||
|
for handle in table.debug_entry_iter() {
|
||||||
|
let id = handle.id();
|
||||||
|
let kind = handle.kind().unwrap();
|
||||||
|
let name = handle.name().unwrap_or("".into());
|
||||||
|
println!("{id:3}: {name:16}| {kind}: {handle}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import_file(table: &mut Table, path: impl AsRef<std::path::Path>) -> Result<(), Box<dyn Error>> {
|
||||||
|
let Ok(file) = std::fs::read_to_string(path.as_ref()) else {
|
||||||
|
for file in std::fs::read_dir(path)? {
|
||||||
|
println!("{}", file?.path().display())
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut parser = Parser::new("", Lexer::new(&file));
|
||||||
|
let code = match parser.parse() {
|
||||||
|
Ok(code) => inline_modules(
|
||||||
|
code,
|
||||||
|
PathBuf::from(path.as_ref()).parent().unwrap_or("".as_ref()),
|
||||||
|
),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{C_ERROR}{}:{e}\x1b[0m", path.as_ref().display());
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let code = cl_ast::desugar::WhileElseDesugar.fold_file(code);
|
||||||
|
Populator::new(table).visit_file(interned(code));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import_files(table: &mut Table) -> Result<(), RlError> {
|
||||||
|
read_and(C_RESV, "fi>", "? >", |line| {
|
||||||
|
let line = line.trim();
|
||||||
|
if line.is_empty() {
|
||||||
|
return Ok(Response::Break);
|
||||||
|
}
|
||||||
|
let Ok(file) = std::fs::read_to_string(line) else {
|
||||||
|
for file in std::fs::read_dir(line)? {
|
||||||
|
println!("{}", file?.path().display())
|
||||||
|
}
|
||||||
|
return Ok(Response::Accept);
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut parser = Parser::new("", Lexer::new(&file));
|
||||||
|
let code = match parser.parse() {
|
||||||
|
Ok(code) => inline_modules(code, PathBuf::from(line).parent().unwrap_or("".as_ref())),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{C_ERROR}{line}:{e}\x1b[0m");
|
||||||
|
return Ok(Response::Deny);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Populator::new(table).visit_file(interned(code));
|
||||||
|
|
||||||
|
println!("...Imported!");
|
||||||
|
Ok(Response::Accept)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pretty_handle(entry: Entry) -> Result<(), std::io::Error> {
|
||||||
|
use std::io::Write;
|
||||||
|
let mut out = std::io::stdout().lock();
|
||||||
|
let Some(kind) = entry.kind() else {
|
||||||
|
return writeln!(out, "{entry}");
|
||||||
|
};
|
||||||
|
write!(out, "{C_LISTING}{kind}")?;
|
||||||
|
|
||||||
|
if let Some(name) = entry.name() {
|
||||||
|
write!(out, " {name}")?;
|
||||||
|
}
|
||||||
|
writeln!(out, "\x1b[0m ({}): {entry}", entry.id())?;
|
||||||
|
|
||||||
|
if let Some(parent) = entry.parent() {
|
||||||
|
writeln!(
|
||||||
|
out,
|
||||||
|
"- {C_LISTING}Parent\x1b[0m: {parent} ({})",
|
||||||
|
parent.id()
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(span) = entry.span() {
|
||||||
|
writeln!(
|
||||||
|
out,
|
||||||
|
"- {C_LISTING}Span:\x1b[0m ({}, {})",
|
||||||
|
span.head, span.tail
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
match entry.meta() {
|
||||||
|
Some(meta) if !meta.is_empty() => {
|
||||||
|
writeln!(out, "- {C_LISTING}Meta:\x1b[0m")?;
|
||||||
|
for meta in meta {
|
||||||
|
writeln!(out, " - {meta}")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(children) = entry.children() {
|
||||||
|
writeln!(out, "- {C_LISTING}Children:\x1b[0m")?;
|
||||||
|
for (name, child) in children {
|
||||||
|
writeln!(
|
||||||
|
out,
|
||||||
|
" - {C_LISTING}{name}\x1b[0m ({child}): {}",
|
||||||
|
entry.with_id(*child)
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(imports) = entry.imports() {
|
||||||
|
writeln!(out, "- {C_LISTING}Imports:\x1b[0m")?;
|
||||||
|
for (name, child) in imports {
|
||||||
|
writeln!(
|
||||||
|
out,
|
||||||
|
" - {C_LISTING}{name}\x1b[0m ({child}): {}",
|
||||||
|
entry.with_id(*child)
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inline_modules(code: cl_ast::File, path: impl AsRef<path::Path>) -> cl_ast::File {
|
||||||
|
match ModuleInliner::new(path).inline(code) {
|
||||||
|
Err((code, io, parse)) => {
|
||||||
|
for (file, error) in io {
|
||||||
|
eprintln!("{}:{error}", file.display());
|
||||||
|
}
|
||||||
|
for (file, error) in parse {
|
||||||
|
eprintln!("{}:{error}", file.display());
|
||||||
|
}
|
||||||
|
code
|
||||||
|
}
|
||||||
|
Ok(code) => code,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump(table: &Table) -> Result<(), Box<dyn Error>> {
|
||||||
|
fn dump_recursive(
|
||||||
|
name: cl_ast::Sym,
|
||||||
|
entry: Entry,
|
||||||
|
depth: usize,
|
||||||
|
to_file: &mut std::fs::File,
|
||||||
|
) -> std::io::Result<()> {
|
||||||
|
use std::io::Write;
|
||||||
|
write!(to_file, "{:w$}{name}: {entry}", "", w = depth)?;
|
||||||
|
if let Some(children) = entry.children() {
|
||||||
|
writeln!(to_file, " {{")?;
|
||||||
|
for (name, child) in children {
|
||||||
|
dump_recursive(*name, entry.with_id(*child), depth + 2, to_file)?;
|
||||||
|
}
|
||||||
|
write!(to_file, "{:w$}}}", "", w = depth)?;
|
||||||
|
}
|
||||||
|
writeln!(to_file)
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file = std::fs::File::create("typeck-table.ron")?;
|
||||||
|
dump_recursive("root".into(), table.root_entry(), 0, &mut file)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear() -> Result<(), Box<dyn Error>> {
|
||||||
|
println!("\x1b[H\x1b[2J");
|
||||||
|
banner();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn banner() {
|
||||||
|
println!(
|
||||||
|
"--- {} v{} 💪🦈 ---",
|
||||||
|
env!("CARGO_BIN_NAME"),
|
||||||
|
env!("CARGO_PKG_VERSION"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interns a [File](cl_ast::File), returning a static reference to it.
|
||||||
|
fn interned(file: cl_ast::File) -> &'static cl_ast::File {
|
||||||
|
use cl_structures::intern::typed_interner::TypedInterner;
|
||||||
|
static INTERNER: LazyLock<TypedInterner<'static, cl_ast::File>> =
|
||||||
|
LazyLock::new(Default::default);
|
||||||
|
|
||||||
|
INTERNER.get_or_insert(file).to_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interns an [Expr](cl_ast::Expr), returning a static reference to it.
|
||||||
|
fn exp_terned(expr: cl_ast::Expr) -> &'static cl_ast::Expr {
|
||||||
|
use cl_structures::intern::typed_interner::TypedInterner;
|
||||||
|
static INTERNER: LazyLock<TypedInterner<'static, cl_ast::Expr>> =
|
||||||
|
LazyLock::new(Default::default);
|
||||||
|
|
||||||
|
INTERNER.get_or_insert(expr).to_ref()
|
||||||
|
}
|
||||||
212
compiler/cl-typeck/src/entry.rs
Normal file
212
compiler/cl-typeck/src/entry.rs
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
//! An [Entry] is an accessor for [nodes](Handle) in a [Table].
|
||||||
|
//!
|
||||||
|
//! There are two kinds of entry:
|
||||||
|
//! - [Entry]: Provides getters for an entry's fields, and an implementation of
|
||||||
|
//! [Display](std::fmt::Display)
|
||||||
|
//! - [EntryMut]: Provides setters for an entry's fields, and an [`as_ref`](EntryMut::as_ref) method
|
||||||
|
//! to demote to an [Entry].
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use cl_ast::{Expr, Meta, PathPart, Sym};
|
||||||
|
use cl_structures::span::Span;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
handle::Handle,
|
||||||
|
source::Source,
|
||||||
|
stage::categorize as cat,
|
||||||
|
table::{NodeKind, Table},
|
||||||
|
type_expression::{self as tex, TypeExpression},
|
||||||
|
type_kind::TypeKind,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod debug;
|
||||||
|
mod display;
|
||||||
|
|
||||||
|
impl Handle {
|
||||||
|
pub const fn to_entry<'t, 'a>(self, table: &'t Table<'a>) -> Entry<'t, 'a> {
|
||||||
|
Entry { id: self, table }
|
||||||
|
}
|
||||||
|
pub fn to_entry_mut<'t, 'a>(self, table: &'t mut Table<'a>) -> EntryMut<'t, 'a> {
|
||||||
|
EntryMut { id: self, table }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Entry<'t, 'a> {
|
||||||
|
table: &'t Table<'a>,
|
||||||
|
id: Handle,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_entry_ {
|
||||||
|
() => {
|
||||||
|
pub const fn id(&self) -> Handle {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn inner(&'t self) -> &'t Table<'a> {
|
||||||
|
self.table
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn kind(&self) -> Option<&NodeKind> {
|
||||||
|
self.table.kind(self.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn root(&self) -> Handle {
|
||||||
|
self.table.root()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn children(&self) -> Option<&HashMap<Sym, Handle>> {
|
||||||
|
self.table.children(self.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn imports(&self) -> Option<&HashMap<Sym, Handle>> {
|
||||||
|
self.table.imports(self.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bodies(&self) -> Option<&'a Expr> {
|
||||||
|
self.table.body(self.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn span(&self) -> Option<&Span> {
|
||||||
|
self.table.span(self.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn meta(&self) -> Option<&[Meta]> {
|
||||||
|
self.table.meta(self.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source(&self) -> Option<&Source<'a>> {
|
||||||
|
self.table.source(self.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> Option<Sym> {
|
||||||
|
self.table.name(self.id)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'t, 'a> Entry<'t, 'a> {
|
||||||
|
pub const fn new(table: &'t Table<'a>, id: Handle) -> Self {
|
||||||
|
Self { table, id }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_entry_!();
|
||||||
|
|
||||||
|
pub const fn with_id(&self, id: Handle) -> Entry<'t, 'a> {
|
||||||
|
Self { table: self.table, id }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nav(&self, path: &[PathPart]) -> Option<Entry<'t, 'a>> {
|
||||||
|
Some(Entry { id: self.table.nav(self.id, path)?, table: self.table })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parent(&self) -> Option<Entry<'t, 'a>> {
|
||||||
|
Some(Entry { id: *self.table.parent(self.id)?, ..*self })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ty(&self) -> Option<&'t TypeKind> {
|
||||||
|
self.table.ty(self.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn impl_target(&self) -> Option<Entry<'_, 'a>> {
|
||||||
|
Some(Entry { id: self.table.impl_target(self.id)?, ..*self })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn selfty(&self) -> Option<Entry<'_, 'a>> {
|
||||||
|
Some(Entry { id: self.table.selfty(self.id)?, ..*self })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct EntryMut<'t, 'a> {
|
||||||
|
table: &'t mut Table<'a>,
|
||||||
|
id: Handle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'t, 'a> EntryMut<'t, 'a> {
|
||||||
|
pub fn new(table: &'t mut Table<'a>, id: Handle) -> Self {
|
||||||
|
Self { table, id }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_entry_!();
|
||||||
|
|
||||||
|
pub fn ty(&self) -> Option<&TypeKind> {
|
||||||
|
self.table.ty(self.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inner_mut(&mut self) -> &mut Table<'a> {
|
||||||
|
self.table
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_ref(&self) -> Entry<'_, 'a> {
|
||||||
|
Entry { table: self.table, id: self.id }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluates a [TypeExpression] in this entry's context
|
||||||
|
pub fn evaluate<Out>(&mut self, ty: &impl TypeExpression<Out>) -> Result<Out, tex::Error> {
|
||||||
|
let Self { table, id } = self;
|
||||||
|
ty.evaluate(table, *id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn categorize(&mut self) -> Result<(), cat::Error> {
|
||||||
|
cat::categorize(self.table, self.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a new Handle with the provided parent [Handle]
|
||||||
|
pub fn with_id(&mut self, parent: Handle) -> EntryMut<'_, 'a> {
|
||||||
|
EntryMut { table: self.table, id: parent }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nav(&mut self, path: &[PathPart]) -> Option<EntryMut<'_, 'a>> {
|
||||||
|
Some(EntryMut { id: self.table.nav(self.id, path)?, table: self.table })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_entry(&mut self, kind: NodeKind) -> EntryMut<'_, 'a> {
|
||||||
|
let id = self.table.new_entry(self.id, kind);
|
||||||
|
self.with_id(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_child(&mut self, name: Sym, child: Handle) -> Option<Handle> {
|
||||||
|
self.table.add_child(self.id, name, child)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_body(&mut self, body: &'a Expr) -> Option<&'a Expr> {
|
||||||
|
self.table.set_body(self.id, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_ty(&mut self, kind: TypeKind) -> Option<TypeKind> {
|
||||||
|
self.table.set_ty(self.id, kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_span(&mut self, span: Span) -> Option<Span> {
|
||||||
|
self.table.set_span(self.id, span)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_meta(&mut self, meta: &'a [Meta]) -> Option<&'a [Meta]> {
|
||||||
|
self.table.set_meta(self.id, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_source(&mut self, source: Source<'a>) -> Option<Source<'a>> {
|
||||||
|
self.table.set_source(self.id, source)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_impl_target(&mut self, target: Handle) -> Option<Handle> {
|
||||||
|
self.table.set_impl_target(self.id, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mark_unchecked(&mut self) {
|
||||||
|
self.table.mark_unchecked(self.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mark_use_item(&mut self) {
|
||||||
|
self.table.mark_use_item(self.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mark_impl_item(&mut self) {
|
||||||
|
self.table.mark_impl_item(self.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mark_lang_item(&mut self, lang_item: &'static str) {
|
||||||
|
self.table.mark_lang_item(lang_item, self.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
33
compiler/cl-typeck/src/entry/debug.rs
Normal file
33
compiler/cl-typeck/src/entry/debug.rs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
//! [std::fmt::Debug] implementation for [Entry]
|
||||||
|
|
||||||
|
use super::Entry;
|
||||||
|
|
||||||
|
impl std::fmt::Debug for Entry<'_, '_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
// virtual fields
|
||||||
|
let mut ds = f.debug_struct("Entry");
|
||||||
|
if let Some(name) = self.name() {
|
||||||
|
ds.field("name", &name.to_ref());
|
||||||
|
}
|
||||||
|
ds.field("kind", &self.kind());
|
||||||
|
if let Some(ty) = self.ty() {
|
||||||
|
ds.field("type", ty);
|
||||||
|
}
|
||||||
|
if let Some(meta) = self.meta() {
|
||||||
|
ds.field("meta", &meta);
|
||||||
|
}
|
||||||
|
if let Some(body) = self.bodies() {
|
||||||
|
ds.field("body", body);
|
||||||
|
}
|
||||||
|
if let Some(children) = self.children() {
|
||||||
|
ds.field("children", children);
|
||||||
|
}
|
||||||
|
if let Some(imports) = self.imports() {
|
||||||
|
ds.field("imports", imports);
|
||||||
|
}
|
||||||
|
// if let Some(source) = self.source() {
|
||||||
|
// ds.field("source", source);
|
||||||
|
// }
|
||||||
|
ds.field("implements", &self.impl_target()).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
100
compiler/cl-typeck/src/entry/display.rs
Normal file
100
compiler/cl-typeck/src/entry/display.rs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::{format_utils::*, type_kind::Adt};
|
||||||
|
use std::fmt::{self, Write};
|
||||||
|
|
||||||
|
/// Printing the name of a named type stops infinite recursion
|
||||||
|
fn write_name_or(h: Entry, f: &mut impl Write) -> fmt::Result {
|
||||||
|
match h.name() {
|
||||||
|
Some(name) => write!(f, "{name}"),
|
||||||
|
None => write!(f, "{h}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Entry<'_, '_> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let Some(&kind) = self.kind() else {
|
||||||
|
return write!(f, "<invalid type: {}>", self.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(ty) = self.ty() {
|
||||||
|
match ty {
|
||||||
|
TypeKind::Inferred => write!(f, "<_{}>", self.id),
|
||||||
|
TypeKind::Variable => write!(f, "<?{}>", self.id),
|
||||||
|
TypeKind::Instance(id) => write!(f, "{}", self.with_id(*id)),
|
||||||
|
TypeKind::Primitive(kind) => write!(f, "{kind}"),
|
||||||
|
TypeKind::Adt(adt) => write_adt(adt, self, f),
|
||||||
|
&TypeKind::Ref(id) => {
|
||||||
|
f.write_str("&")?;
|
||||||
|
let h_id = self.with_id(id);
|
||||||
|
write_name_or(h_id, f)
|
||||||
|
}
|
||||||
|
&TypeKind::Ptr(id) => {
|
||||||
|
f.write_str("*")?;
|
||||||
|
let h_id = self.with_id(id);
|
||||||
|
write_name_or(h_id, f)
|
||||||
|
}
|
||||||
|
TypeKind::Slice(id) => {
|
||||||
|
write_name_or(self.with_id(*id), &mut f.delimit_with("[", "]"))
|
||||||
|
}
|
||||||
|
&TypeKind::Array(t, cnt) => {
|
||||||
|
let mut f = f.delimit_with("[", "]");
|
||||||
|
write_name_or(self.with_id(t), &mut f)?;
|
||||||
|
write!(f, "; {cnt}")
|
||||||
|
}
|
||||||
|
TypeKind::Tuple(ids) => {
|
||||||
|
let mut f = f.delimit_with("(", ")");
|
||||||
|
for (index, &id) in ids.iter().enumerate() {
|
||||||
|
if index > 0 {
|
||||||
|
write!(f, ", ")?;
|
||||||
|
}
|
||||||
|
write_name_or(self.with_id(id), &mut f)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
TypeKind::FnSig { args, rety } => {
|
||||||
|
write!(f, "fn {} -> ", self.with_id(*args))?;
|
||||||
|
write_name_or(self.with_id(*rety), f)
|
||||||
|
}
|
||||||
|
TypeKind::Module => write!(f, "module?"),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match kind {
|
||||||
|
NodeKind::Type
|
||||||
|
| NodeKind::Const
|
||||||
|
| NodeKind::Static
|
||||||
|
| NodeKind::Temporary
|
||||||
|
| NodeKind::Let => write!(f, "WARNING: NO TYPE ASSIGNED FOR {}", self.id),
|
||||||
|
_ => write!(f, "{kind}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_adt(adt: &Adt, h: &Entry, f: &mut impl Write) -> fmt::Result {
|
||||||
|
match adt {
|
||||||
|
Adt::Enum(variants) => {
|
||||||
|
let mut variants = variants.iter();
|
||||||
|
separate(", ", || {
|
||||||
|
variants.next().map(|(name, def)| {
|
||||||
|
move |f: &mut Delimit<_>| write!(f, "{name}: {}", h.with_id(*def))
|
||||||
|
})
|
||||||
|
})(f.delimit_with("enum {", "}"))
|
||||||
|
}
|
||||||
|
Adt::Struct(members) => {
|
||||||
|
let mut members = members.iter();
|
||||||
|
separate(", ", || {
|
||||||
|
let (name, vis, id) = members.next()?;
|
||||||
|
Some(move |f: &mut Delimit<_>| write!(f, "{vis}{name}: {}", h.with_id(*id)))
|
||||||
|
})(f.delimit_with("struct {", "}"))
|
||||||
|
}
|
||||||
|
Adt::TupleStruct(members) => {
|
||||||
|
let mut members = members.iter();
|
||||||
|
separate(", ", || {
|
||||||
|
let (vis, def) = members.next()?;
|
||||||
|
Some(move |f: &mut Delimit<_>| write!(f, "{vis}{}", h.with_id(*def)))
|
||||||
|
})(f.delimit_with("struct (", ")"))
|
||||||
|
}
|
||||||
|
Adt::UnitStruct => write!(f, "struct"),
|
||||||
|
Adt::Union(_) => todo!("Display union types"),
|
||||||
|
}
|
||||||
|
}
|
||||||
20
compiler/cl-typeck/src/format_utils.rs
Normal file
20
compiler/cl-typeck/src/format_utils.rs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
pub use cl_ast::format::*;
|
||||||
|
use std::{fmt, iter};
|
||||||
|
|
||||||
|
/// Separates the items yielded by iterating the provided function
|
||||||
|
pub const fn separate<'f, 's, Item, F, W>(sep: &'s str, t: F) -> impl FnOnce(W) -> fmt::Result + 's
|
||||||
|
where
|
||||||
|
Item: FnMut(&mut W) -> fmt::Result,
|
||||||
|
F: FnMut() -> Option<Item> + 's,
|
||||||
|
W: fmt::Write,
|
||||||
|
{
|
||||||
|
move |mut f| {
|
||||||
|
for (idx, mut disp) in iter::from_fn(t).enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
f.write_str(sep)?;
|
||||||
|
}
|
||||||
|
disp(&mut f)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
15
compiler/cl-typeck/src/handle.rs
Normal file
15
compiler/cl-typeck/src/handle.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
//! A [Handle] uniquely represents an entry in the [Table](crate::table::Table)
|
||||||
|
|
||||||
|
use cl_structures::index_map::*;
|
||||||
|
|
||||||
|
// define the index types
|
||||||
|
make_index! {
|
||||||
|
/// Uniquely represents an entry in the [Table](crate::table::Table)
|
||||||
|
Handle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Handle {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
74
compiler/cl-typeck/src/lib.rs
Normal file
74
compiler/cl-typeck/src/lib.rs
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
//! # The Conlang Type Checker
|
||||||
|
//!
|
||||||
|
//! As a statically typed language, Conlang requires a robust type checker to enforce correctness.
|
||||||
|
//!
|
||||||
|
//! This crate is a major work-in-progress.
|
||||||
|
//!
|
||||||
|
//! # The [Table](table::Table)™
|
||||||
|
//! A directed graph of nodes and their dependencies.
|
||||||
|
//!
|
||||||
|
//! Contains [item definitions](handle) and [type expression](type_expression) information.
|
||||||
|
//!
|
||||||
|
//! *Every* item is itself a module, and can contain arbitrarily nested items
|
||||||
|
//! as part of the item graph
|
||||||
|
//!
|
||||||
|
//! The table, additionally, has some queues for use in external algorithms,
|
||||||
|
//! detailed in the [stage] module.
|
||||||
|
//!
|
||||||
|
//! # Namespaces
|
||||||
|
//! Each item in the graph is given its own namespace, which is further separated into
|
||||||
|
//! two distinct parts:
|
||||||
|
//! - Children of an item are direct descendents (i.e. their `parent` is a handle to the item)
|
||||||
|
//! - Imports of an item are indirect descendents created by `use` or `impl` directives. They are
|
||||||
|
//! shadowed by Children with the same name.
|
||||||
|
//!
|
||||||
|
//! # Order of operations:
|
||||||
|
//! For order-of-operations information, see the [stage] module.
|
||||||
|
#![warn(clippy::all)]
|
||||||
|
|
||||||
|
pub(crate) mod format_utils;
|
||||||
|
|
||||||
|
pub mod table;
|
||||||
|
|
||||||
|
pub mod handle;
|
||||||
|
|
||||||
|
pub mod entry;
|
||||||
|
|
||||||
|
pub mod source;
|
||||||
|
|
||||||
|
pub mod type_kind;
|
||||||
|
|
||||||
|
pub mod type_expression;
|
||||||
|
|
||||||
|
pub mod stage {
|
||||||
|
//! Type collection, evaluation, checking, and inference passes.
|
||||||
|
//!
|
||||||
|
//! # Order of operations
|
||||||
|
//! 1. [mod@populate]: Populate the graph with nodes for every named item.
|
||||||
|
//! 2. [mod@import]: Import the `use` nodes discovered in [Stage 1](populate).
|
||||||
|
//! 3. [mod@categorize]: Categorize the nodes according to textual type information.
|
||||||
|
//! - Creates anonymous types (`fn(T) -> U`, `&T`, `[T]`, etc.) as necessary to fill in the
|
||||||
|
//! type graph
|
||||||
|
//! - Creates a new struct type for every enum struct-variant.
|
||||||
|
//! 4. [mod@implement]: Import members of implementation modules into types.
|
||||||
|
|
||||||
|
pub use populate::Populator;
|
||||||
|
/// Stage 1: Populate the graph with nodes.
|
||||||
|
pub mod populate;
|
||||||
|
|
||||||
|
pub use import::import;
|
||||||
|
/// Stage 2: Import the `use` nodes discovered in Stage 1.
|
||||||
|
pub mod import;
|
||||||
|
|
||||||
|
pub use categorize::categorize;
|
||||||
|
/// Stage 3: Categorize the nodes according to textual type information.
|
||||||
|
pub mod categorize;
|
||||||
|
|
||||||
|
pub use implement::implement;
|
||||||
|
/// Stage 4: Import members of `impl` blocks into their corresponding types.
|
||||||
|
pub mod implement;
|
||||||
|
|
||||||
|
// TODO: Make type inference stage 5
|
||||||
|
// TODO: Use the type information stored in the [table]
|
||||||
|
pub mod infer;
|
||||||
|
}
|
||||||
87
compiler/cl-typeck/src/source.rs
Normal file
87
compiler/cl-typeck/src/source.rs
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
//! Holds the [Source] of a definition in the AST
|
||||||
|
|
||||||
|
use cl_ast::ast::*;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Source<'a> {
|
||||||
|
Root,
|
||||||
|
Module(&'a Module),
|
||||||
|
Alias(&'a Alias),
|
||||||
|
Enum(&'a Enum),
|
||||||
|
Variant(&'a Variant),
|
||||||
|
Struct(&'a Struct),
|
||||||
|
Const(&'a Const),
|
||||||
|
Static(&'a Static),
|
||||||
|
Function(&'a Function),
|
||||||
|
Local(&'a Let),
|
||||||
|
Impl(&'a Impl),
|
||||||
|
Use(&'a Use),
|
||||||
|
Ty(&'a TyKind),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Source<'_> {
|
||||||
|
pub fn name(&self) -> Option<Sym> {
|
||||||
|
match self {
|
||||||
|
Source::Root => None,
|
||||||
|
Source::Module(v) => Some(v.name),
|
||||||
|
Source::Alias(v) => Some(v.name),
|
||||||
|
Source::Enum(v) => Some(v.name),
|
||||||
|
Source::Variant(v) => Some(v.name),
|
||||||
|
Source::Struct(v) => Some(v.name),
|
||||||
|
Source::Const(v) => Some(v.name),
|
||||||
|
Source::Static(v) => Some(v.name),
|
||||||
|
Source::Function(v) => Some(v.name),
|
||||||
|
Source::Local(_) => None,
|
||||||
|
Source::Impl(_) | Source::Use(_) | Source::Ty(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this [Source] defines a named value
|
||||||
|
pub fn is_named_value(&self) -> bool {
|
||||||
|
matches!(self, Self::Const(_) | Self::Static(_) | Self::Function(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this [Source] defines a named type
|
||||||
|
pub fn is_named_type(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::Module(_) | Self::Alias(_) | Self::Enum(_) | Self::Struct(_)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this [Source] refers to a [Ty] with no name
|
||||||
|
pub fn is_anon_type(&self) -> bool {
|
||||||
|
matches!(self, Self::Ty(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this [Source] refers to an [Impl] block
|
||||||
|
pub fn is_impl(&self) -> bool {
|
||||||
|
matches!(self, Self::Impl(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this [Source] refers to a [Use] import
|
||||||
|
pub fn is_use_import(&self) -> bool {
|
||||||
|
matches!(self, Self::Use(_))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Source<'_> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Root => "🌳 root 🌳".fmt(f),
|
||||||
|
Self::Module(arg0) => arg0.fmt(f),
|
||||||
|
Self::Alias(arg0) => arg0.fmt(f),
|
||||||
|
Self::Enum(arg0) => arg0.fmt(f),
|
||||||
|
Self::Variant(arg0) => arg0.fmt(f),
|
||||||
|
Self::Struct(arg0) => arg0.fmt(f),
|
||||||
|
Self::Const(arg0) => arg0.fmt(f),
|
||||||
|
Self::Static(arg0) => arg0.fmt(f),
|
||||||
|
Self::Function(arg0) => arg0.fmt(f),
|
||||||
|
Self::Impl(arg0) => arg0.fmt(f),
|
||||||
|
Self::Use(arg0) => arg0.fmt(f),
|
||||||
|
Self::Ty(arg0) => arg0.fmt(f),
|
||||||
|
Self::Local(arg0) => arg0.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
187
compiler/cl-typeck/src/stage/categorize.rs
Normal file
187
compiler/cl-typeck/src/stage/categorize.rs
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
//! Categorizes an entry in a table according to its embedded type information
|
||||||
|
#![allow(unused)]
|
||||||
|
use crate::{
|
||||||
|
entry::EntryMut,
|
||||||
|
handle::Handle,
|
||||||
|
source::Source,
|
||||||
|
table::{NodeKind, Table},
|
||||||
|
type_expression::{Error as TypeEval, TypeExpression},
|
||||||
|
type_kind::{Adt, TypeKind},
|
||||||
|
};
|
||||||
|
use cl_ast::*;
|
||||||
|
|
||||||
|
/// Ensures a type entry exists for the provided handle in the table
|
||||||
|
pub fn categorize(table: &mut Table, node: Handle) -> CatResult<()> {
|
||||||
|
let Some(source) = table.source(node) else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
match source {
|
||||||
|
Source::Variant(v) => cat_variant(table, node, v)?,
|
||||||
|
Source::Struct(s) => cat_struct(table, node, s)?,
|
||||||
|
Source::Const(c) => cat_const(table, node, c)?,
|
||||||
|
Source::Static(s) => cat_static(table, node, s)?,
|
||||||
|
Source::Function(f) => cat_function(table, node, f)?,
|
||||||
|
Source::Local(l) => cat_local(table, node, l)?,
|
||||||
|
Source::Impl(i) => cat_impl(table, node, i)?,
|
||||||
|
// Source::Alias(_) => {table.mark_unchecked(node)},
|
||||||
|
_ => return Ok(()),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parent(table: &Table, node: Handle) -> Handle {
|
||||||
|
table.parent(node).copied().unwrap_or(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cat_struct(table: &mut Table, node: Handle, s: &Struct) -> CatResult<()> {
|
||||||
|
let Struct { name: _, gens: _, kind } = s;
|
||||||
|
// TODO: Generics
|
||||||
|
let kind = match kind {
|
||||||
|
StructKind::Empty => TypeKind::Adt(Adt::UnitStruct),
|
||||||
|
StructKind::Tuple(types) => {
|
||||||
|
let mut out = vec![];
|
||||||
|
for ty in types {
|
||||||
|
out.push((Visibility::Public, ty.evaluate(table, node)?))
|
||||||
|
}
|
||||||
|
TypeKind::Adt(Adt::TupleStruct(out))
|
||||||
|
}
|
||||||
|
StructKind::Struct(members) => {
|
||||||
|
let mut out = vec![];
|
||||||
|
for m in members {
|
||||||
|
out.push(cat_member(table, node, m)?)
|
||||||
|
}
|
||||||
|
TypeKind::Adt(Adt::Struct(out))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
table.set_ty(node, kind);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cat_member(
|
||||||
|
table: &mut Table,
|
||||||
|
node: Handle,
|
||||||
|
m: &StructMember,
|
||||||
|
) -> CatResult<(Sym, Visibility, Handle)> {
|
||||||
|
let StructMember { vis, name, ty } = m;
|
||||||
|
Ok((*name, *vis, ty.evaluate(table, node)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cat_enum<'a>(_table: &mut Table<'a>, _node: Handle, e: &'a Enum) -> CatResult<()> {
|
||||||
|
let Enum { name: _, gens: _, variants: _ } = e;
|
||||||
|
// table.set_ty(node, kind);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cat_variant<'a>(table: &mut Table<'a>, node: Handle, v: &'a Variant) -> CatResult<()> {
|
||||||
|
let Variant { name, kind, body } = v;
|
||||||
|
let parent = table.parent(node).copied().unwrap_or(table.root());
|
||||||
|
match (kind) {
|
||||||
|
(StructKind::Empty) => Ok(()),
|
||||||
|
(StructKind::Empty) => Ok(()),
|
||||||
|
(StructKind::Tuple(ty)) => {
|
||||||
|
let ty = TypeKind::Adt(Adt::TupleStruct(
|
||||||
|
ty.iter()
|
||||||
|
.map(|ty| ty.evaluate(table, node).map(|ty| (Visibility::Public, ty)))
|
||||||
|
.collect::<Result<_, _>>()?,
|
||||||
|
));
|
||||||
|
table.set_ty(node, ty);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
(StructKind::Struct(members)) => {
|
||||||
|
let mut out = vec![];
|
||||||
|
for StructMember { vis, name, ty } in members {
|
||||||
|
let ty = ty.evaluate(table, node)?;
|
||||||
|
out.push((*name, *vis, ty));
|
||||||
|
|
||||||
|
let mut this = node.to_entry_mut(table);
|
||||||
|
let mut child = this.new_entry(NodeKind::Type);
|
||||||
|
child.set_source(Source::Variant(v));
|
||||||
|
child.set_ty(TypeKind::Instance(ty));
|
||||||
|
|
||||||
|
let child = child.id();
|
||||||
|
this.add_child(*name, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
table.set_ty(node, TypeKind::Adt(Adt::Struct(out)));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cat_const(table: &mut Table, node: Handle, c: &Const) -> CatResult<()> {
|
||||||
|
let parent = parent(table, node);
|
||||||
|
let kind = TypeKind::Instance(
|
||||||
|
c.ty.evaluate(table, parent)
|
||||||
|
.map_err(|e| Error::TypeEval(e, " while categorizing a const"))?,
|
||||||
|
);
|
||||||
|
table.set_ty(node, kind);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cat_static(table: &mut Table, node: Handle, s: &Static) -> CatResult<()> {
|
||||||
|
let parent = parent(table, node);
|
||||||
|
let kind = TypeKind::Instance(
|
||||||
|
s.ty.evaluate(table, parent)
|
||||||
|
.map_err(|e| Error::TypeEval(e, " while categorizing a static"))?,
|
||||||
|
);
|
||||||
|
table.set_ty(node, kind);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cat_function(table: &mut Table, node: Handle, f: &Function) -> CatResult<()> {
|
||||||
|
let kind = TypeKind::Instance(
|
||||||
|
f.sign
|
||||||
|
.evaluate(table, node)
|
||||||
|
.map_err(|e| Error::TypeEval(e, " while categorizing a function"))?,
|
||||||
|
);
|
||||||
|
table.set_ty(node, kind);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cat_local(table: &mut Table, node: Handle, l: &Let) -> CatResult<()> {
|
||||||
|
let parent = parent(table, node);
|
||||||
|
if let Some(ty) = &l.ty {
|
||||||
|
let kind = ty
|
||||||
|
.evaluate(table, parent)
|
||||||
|
.map_err(|e| Error::TypeEval(e, " while categorizing a let binding"))?;
|
||||||
|
table.set_ty(node, TypeKind::Instance(kind));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cat_impl(table: &mut Table, node: Handle, i: &Impl) -> CatResult<()> {
|
||||||
|
let parent = parent(table, node);
|
||||||
|
let Impl { gens, target, body: _ } = i;
|
||||||
|
let target = match target {
|
||||||
|
ImplKind::Type(t) => t.evaluate(table, parent),
|
||||||
|
ImplKind::Trait { impl_trait: _, for_type: t } => t.evaluate(table, parent),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
table.set_impl_target(node, target);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
type CatResult<T> = Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
BadMeta(Meta),
|
||||||
|
TypeEval(TypeEval, &'static str),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TypeEval> for Error {
|
||||||
|
fn from(value: TypeEval) -> Self {
|
||||||
|
Error::TypeEval(value, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::BadMeta(meta) => write!(f, "Unknown attribute: #[{meta}]"),
|
||||||
|
Error::TypeEval(e, during) => write!(f, "{e}{during}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
compiler/cl-typeck/src/stage/implement.rs
Normal file
23
compiler/cl-typeck/src/stage/implement.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
use crate::{handle::Handle, table::Table};
|
||||||
|
|
||||||
|
pub fn implement(table: &mut Table) -> Vec<Handle> {
|
||||||
|
let pending = std::mem::take(&mut table.impls);
|
||||||
|
let mut errors = vec![];
|
||||||
|
for node in pending {
|
||||||
|
if let Err(e) = impl_one(table, node) {
|
||||||
|
errors.push(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errors
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn impl_one(table: &mut Table, node: Handle) -> Result<(), Handle> {
|
||||||
|
let Some(target) = table.impl_target(node) else {
|
||||||
|
Err(node)?
|
||||||
|
};
|
||||||
|
if let Some(children) = table.children.get_mut(&node) {
|
||||||
|
let children = children.clone();
|
||||||
|
table.children.entry(target).or_default().extend(children);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
158
compiler/cl-typeck/src/stage/import.rs
Normal file
158
compiler/cl-typeck/src/stage/import.rs
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
//! An algorithm for importing external nodes
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
handle::Handle,
|
||||||
|
source::Source,
|
||||||
|
table::{NodeKind, Table},
|
||||||
|
};
|
||||||
|
use cl_ast::{PathPart, Sym, Use, UseTree};
|
||||||
|
use core::slice;
|
||||||
|
use std::{collections::HashSet, mem};
|
||||||
|
|
||||||
|
type Seen = HashSet<Handle>;
|
||||||
|
|
||||||
|
pub fn import<'a>(table: &mut Table<'a>) -> Vec<(Handle, Error<'a>)> {
|
||||||
|
let pending = mem::take(&mut table.uses);
|
||||||
|
|
||||||
|
let mut seen = Seen::new();
|
||||||
|
let mut failed = vec![];
|
||||||
|
for import in pending {
|
||||||
|
let Err(e) = import_one(table, import, &mut seen) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if let Error::NotFound(_, _) = e {
|
||||||
|
table.mark_use_item(import)
|
||||||
|
}
|
||||||
|
failed.push((import, e));
|
||||||
|
}
|
||||||
|
failed
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import_one<'a>(table: &mut Table<'a>, item: Handle, seen: &mut Seen) -> UseResult<'a, ()> {
|
||||||
|
if !seen.insert(item) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(NodeKind::Use) = table.kind(item) else {
|
||||||
|
Err(Error::ItsNoUse)?
|
||||||
|
};
|
||||||
|
let Some(&dst) = table.parent(item) else {
|
||||||
|
Err(Error::NoParents)?
|
||||||
|
};
|
||||||
|
let Some(code) = table.source(item) else {
|
||||||
|
Err(Error::NoSource)?
|
||||||
|
};
|
||||||
|
let &Source::Use(tree) = code else {
|
||||||
|
Err(Error::BadSource(*code))?
|
||||||
|
};
|
||||||
|
let Use { absolute, tree } = tree;
|
||||||
|
|
||||||
|
import_tree(
|
||||||
|
table,
|
||||||
|
if !absolute { dst } else { table.root() },
|
||||||
|
dst,
|
||||||
|
tree,
|
||||||
|
seen,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import_tree<'a>(
|
||||||
|
table: &mut Table<'a>,
|
||||||
|
src: Handle,
|
||||||
|
dst: Handle,
|
||||||
|
tree: &UseTree,
|
||||||
|
seen: &mut Seen,
|
||||||
|
) -> UseResult<'a, ()> {
|
||||||
|
match tree {
|
||||||
|
UseTree::Tree(trees) => trees
|
||||||
|
.iter()
|
||||||
|
.try_for_each(|tree| import_tree(table, src, dst, tree, seen)),
|
||||||
|
UseTree::Path(part, rest) => {
|
||||||
|
let source = table
|
||||||
|
.nav(src, slice::from_ref(part))
|
||||||
|
.ok_or(Error::NotFound(src, *part))?;
|
||||||
|
import_tree(table, source, dst, rest, seen)
|
||||||
|
}
|
||||||
|
UseTree::Alias(src_name, dst_name) => {
|
||||||
|
import_name(table, src, src_name, dst, dst_name, seen)
|
||||||
|
}
|
||||||
|
UseTree::Name(src_name) => import_name(table, src, src_name, dst, src_name, seen),
|
||||||
|
UseTree::Glob => import_glob(table, src, dst, seen),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import_glob<'a>(
|
||||||
|
table: &mut Table<'a>,
|
||||||
|
src: Handle,
|
||||||
|
dst: Handle,
|
||||||
|
seen: &mut Seen,
|
||||||
|
) -> UseResult<'a, ()> {
|
||||||
|
let Table { children, imports, .. } = table;
|
||||||
|
|
||||||
|
if let Some(c) = children.get(&src) {
|
||||||
|
imports.entry(dst).or_default().extend(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
import_deps(table, src, seen)?;
|
||||||
|
|
||||||
|
let Table { imports, .. } = table;
|
||||||
|
|
||||||
|
// Importing imports requires some extra work, since we can't `get_many_mut`
|
||||||
|
if let Some(i) = imports.get(&src) {
|
||||||
|
let uses: Vec<_> = i.iter().map(|(&k, &v)| (k, v)).collect();
|
||||||
|
imports.entry(dst).or_default().extend(uses);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import_name<'a>(
|
||||||
|
table: &mut Table<'a>,
|
||||||
|
src: Handle,
|
||||||
|
src_name: &Sym,
|
||||||
|
dst: Handle,
|
||||||
|
dst_name: &Sym,
|
||||||
|
seen: &mut Seen,
|
||||||
|
) -> UseResult<'a, ()> {
|
||||||
|
import_deps(table, src, seen)?;
|
||||||
|
match table.get_by_sym(src, src_name) {
|
||||||
|
// TODO: check for new imports clobbering existing imports
|
||||||
|
Some(src_id) => table.add_import(dst, *dst_name, src_id),
|
||||||
|
None => Err(Error::NotFound(src, PathPart::Ident(*src_name)))?,
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Imports the dependencies of this node
|
||||||
|
fn import_deps<'a>(table: &mut Table<'a>, node: Handle, seen: &mut Seen) -> UseResult<'a, ()> {
|
||||||
|
if let Some(items) = table.use_items.get(&node) {
|
||||||
|
let out = items.clone();
|
||||||
|
for item in out {
|
||||||
|
import_one(table, item, seen)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type UseResult<'a, T> = Result<T, Error<'a>>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error<'a> {
|
||||||
|
ItsNoUse,
|
||||||
|
NoParents,
|
||||||
|
NoSource,
|
||||||
|
BadSource(Source<'a>),
|
||||||
|
NotFound(Handle, PathPart),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Error<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::ItsNoUse => write!(f, "Entry is not use"),
|
||||||
|
Error::NoParents => write!(f, "Entry has no parents"),
|
||||||
|
Error::NoSource => write!(f, "Entry has no source"),
|
||||||
|
Error::BadSource(s) => write!(f, "Entry incorrectly marked as use item: {s}"),
|
||||||
|
Error::NotFound(id, part) => write!(f, "Could not traverse {id}::{part}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user