Compare commits

..

No commits in common. "bottom-up-value-place" and "main" have entirely different histories.

29 changed files with 2923 additions and 8293 deletions

1
.envrc
View file

@ -1 +0,0 @@
use flake

1
.gitignore vendored
View file

@ -1,3 +1,2 @@
/target
/Cargo.lock
/.direnv/

View file

@ -1,13 +1,7 @@
[workspace]
resolver = "3"
members = [
"crates/lexer"
, "crates/parser"]
[package]
name = "compiler"
version = "0.1.0"
edition = "2024"
edition = "2021"
[dependencies]
ansi_term = "0.12.1"
@ -18,20 +12,6 @@ log = "0.4.22"
num-bigint = "0.4.6"
num-traits = "0.2.19"
ordered-float = "4.2.2"
paste = "1.0.15"
petgraph = "0.6.5"
thiserror = "1.0.63"
unicode-xid = "0.2.4"
tracing = "0.1.41"
werkzeug = { path = "../../rust/werkzeug" }
[workspace.dependencies]
unicode-xid = "0.2.4"
tracing = "0.1.41"
thiserror = "1.0.63"
itertools = "0.13.0"
werkzeug = { path = "../../rust/werkzeug" }
trie = { path = "../../rust/trie" }

View file

@ -1,14 +0,0 @@
[package]
name = "lexer"
version = "0.1.0"
edition = "2024"
[dependencies]
tracing = { workspace = true }
werkzeug = { workspace = true }
thiserror = { workspace = true }
itertools = { workspace = true }
trie = { workspace = true }
unicode-xid = { workspace = true }
variadics_please = "1.1.0"

View file

@ -1,473 +0,0 @@
use crate::{Source, Token, is_things};
use itertools::Itertools;
use werkzeug::iter::{FallibleMapIter, NextIf};
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
pub enum Error {
#[error("{0}")]
StringError(String),
#[error("Exp part of floating constant had no digits.")]
FloatingConstantExpPartNoDigit,
#[error("constant cannot start with leading underscore '_'.")]
NumericalConstantDigitLeadingUnderscore,
#[error("Expected digit here for constant.")]
NumericalConstantDigitNoDigit,
#[error("Expected digit here for integer constant.")]
IntegralTypeExpectedDigit,
#[error("Floating constant has invalid trailing type.")]
FloatingConstantInvalidTrailingType,
#[error("Invalid token.")]
InvalidToken,
#[error("Unknown suffix in constant.")]
NumericalConstantUnknownSuffix,
}
type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Radix {
Hex,
Bin,
Dec,
Oct,
}
impl Radix {
#[allow(unused)]
/// must be called with one of `['b','x','d','o']`
unsafe fn from_char_unchecked(c: char) -> Self {
match c.to_ascii_lowercase() {
'o' => Self::Oct,
'b' => Self::Bin,
'x' => Self::Hex,
'd' => Self::Dec,
_ => unreachable!(),
}
}
fn from_char(c: char) -> Option<Self> {
match c.to_ascii_lowercase() {
'o' => Some(Self::Oct),
'b' => Some(Self::Bin),
'x' => Some(Self::Hex),
'd' => Some(Self::Dec),
_ => None,
}
}
pub fn from_token(token: Token) -> Option<Self> {
match token {
Token::IntegerHexConstant(_) => Some(Radix::Hex),
Token::IntegerBinConstant(_) => Some(Radix::Bin),
Token::IntegerOctConstant(_) => Some(Radix::Oct),
Token::IntegerConstant(_) => Some(Radix::Dec),
_ => None,
}
}
#[allow(unused)]
pub fn radix(self) -> u8 {
match self {
Radix::Hex => 16,
Radix::Bin => 2,
Radix::Oct => 8,
Radix::Dec => 10,
}
}
fn to_constant_kind(self) -> ConstantKind {
match self {
Radix::Hex => ConstantKind::HexInteger,
Radix::Bin => ConstantKind::BinInteger,
Radix::Oct => ConstantKind::OctInteger,
Radix::Dec => ConstantKind::Integer,
}
}
#[expect(dead_code)]
pub fn map_digit(self, c: char) -> u8 {
match self {
Radix::Hex => match c {
'0'..='9' => c as u8 - b'0',
'a'..='f' => 10 + c as u8 - b'a',
'A'..='F' => 10 + c as u8 - b'A',
_ => unreachable!(),
},
Radix::Bin => match c {
'0'..='1' => c as u8 - b'0',
_ => unreachable!(),
},
Radix::Dec => match c {
'0'..='9' => c as u8 - b'0',
_ => unreachable!(),
},
Radix::Oct => match c {
'0'..='7' => c as u8 - b'0',
_ => unreachable!(),
},
}
}
#[expect(dead_code)]
pub fn folding_method(self) -> fn(u64, char) -> u64 {
match self {
Radix::Hex => {
fn fold(acc: u64, c: char) -> u64 {
let digit = match c {
'0'..='9' => c as u8 - b'0',
'a'..='f' => c as u8 - b'a',
'A'..='F' => c as u8 - b'A',
_ => unreachable!(),
};
acc * 16 + digit as u64
}
fold
}
Radix::Bin => {
fn fold(acc: u64, c: char) -> u64 {
let digit = match c {
'0'..='1' => c as u8 - b'0',
_ => unreachable!(),
};
acc * 2 + digit as u64
}
fold
}
Radix::Dec => {
fn fold(acc: u64, c: char) -> u64 {
let digit = match c {
'0'..='9' => c as u8 - b'0',
_ => unreachable!(),
};
acc * 10 + digit as u64
}
fold
}
Radix::Oct => {
fn fold(acc: u64, c: char) -> u64 {
let digit = match c {
'0'..='7' => c as u8 - b'0',
_ => unreachable!(),
};
acc * 8 + digit as u64
}
fold
}
}
}
pub fn is_digit(self) -> fn(char) -> bool {
match self {
Radix::Hex => is_things::is_hex_digit,
Radix::Bin => is_things::is_bin_digit,
Radix::Oct => is_things::is_oct_digit,
Radix::Dec => is_things::is_digit,
}
}
}
// where DIGIT is defined by radix:
// DIGITS <-
// if allow_leading_underscore: `_`* DIGIT (DIGIT|`_`)*
// else: DIGIT (DIGIT|`_`)*
fn parse_digit_part(
source: &mut Source,
allow_leading_underscore: bool,
radix: Radix,
) -> Result<()> {
let is_digit = radix.is_digit();
if allow_leading_underscore {
let _underscore = source.take_while_ref(|&c| c == '_').count();
}
let _need_digit = source.next_if(|&c| is_digit(c)).ok_or_else(|| {
if source.peek() == Some(&'_') {
Error::NumericalConstantDigitLeadingUnderscore
} else {
Error::NumericalConstantDigitNoDigit
}
})?;
let _rest = source.take_while_ref(|&c| is_digit(c) || c == '_').count();
Ok(())
}
// IntegralType <-
// ( 'u' | 'i' ) DIGITS+
fn try_parse_integral_type(source: &mut Source) -> Result<Option<()>> {
if !source.next_if(|&c| c == 'u' || c == 'i').is_some() {
return Ok(None);
}
if source.take_while_ref(|&c| is_things::is_digit(c)).count() <= 0 {
return Err(Error::IntegralTypeExpectedDigit);
};
Ok(Some(()))
}
// returns `Err(E)` if it failed to parse.
// returns `Ok(None)` if no exp part was found.
// returns `Ok(Some(()))` if an exp part was found and parsed.
//
// EXP_PART <-
// (`e`|`E`) (`-`|`+`)? DEC_DIGITS
fn try_parse_exp_part(source: &mut Source) -> Result<Option<()>> {
if source.next_if(|&c| c.to_ascii_lowercase() == 'e').is_some() {
let _sign = source.next_if(|&c| c == '-' || c == '+');
if source
.take_while_ref(|&c| is_things::is_digit(c))
.count()
.lt(&1)
{
// need digits following exp notation
Err(Error::FloatingConstantExpPartNoDigit)
} else {
Ok(Some(()))
}
} else {
Ok(None)
}
}
// CONSTANT <-
// DEC_DIGITS IntegralType?
// `0x` HEX_DIGITS IntegralType?
// `0b` BIN_DIGITS IntegralType?
// `0o` OCT_DIGITS IntegralType?
// DEC_DIGITS FloatingType?
// `.` DEC_DIGITS EXP_PART? FloatingType?
// DEC_DIGITS `.` DEC_DIGITS? EXP_PART? FloatingType?
fn parse_constant_inner(source: &mut Source) -> Result<ConstantKind> {
let start = source.count;
let zero = source.next_if(|&c| c == '0').is_some();
let radix = zero
.then(|| source.next_if_map(|c| Radix::from_char(c)))
.flatten();
if let Some(radix) = radix {
parse_digit_part(source, false, radix)?;
if source.peek().map(|&c| c == 'u' || c == 'i') == Some(true) {
try_parse_integral_type(source)?;
}
return Ok(radix.to_constant_kind());
}
// if zero: `_`* DIGIT (DIGIT|`_`)*
// else: DIGIT (DIGIT|`_`)*
_ = match parse_digit_part(source, zero, Radix::Dec) {
Ok(_) => Ok(()),
Err(Error::NumericalConstantDigitNoDigit) if zero => Ok(()),
Err(e) => Err(e),
}?;
if let Some(_) = source.try_map_iter_if(|source| try_parse_integral_type(source))? {
return Ok(ConstantKind::Integer);
}
let dot = source.next_if(|&c| c == '.').is_some();
if dot {
parse_digit_part(source, false, Radix::Dec)?;
}
// parse exp notation
let exp = try_parse_exp_part(source)?.is_some();
// trailing FloatingType?
let trailing_float_type = if source.next_if(|&c| c == 'f').is_some() {
let digits = source.next_tuple::<(char, char)>();
if !(digits == Some(('6', '4')) || digits == Some(('3', '2'))) {
// need either f64 or f32 here!
return Err(Error::FloatingConstantInvalidTrailingType);
}
true
} else {
false
};
let token = match (dot, exp, trailing_float_type) {
(false, false, false) => ConstantKind::Integer,
(true, false, _) => ConstantKind::DotFloating,
(true, true, _) => ConstantKind::DotFloatingExp,
(false, true, _) => ConstantKind::FloatingExp,
(false, false, _) => ConstantKind::Floating,
};
Ok(token)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConstantKind {
Integer,
BinInteger,
OctInteger,
HexInteger,
DotFloating,
DotFloatingExp,
FloatingExp,
Floating,
Char,
String,
}
impl<'a> From<(ConstantKind, &'a str)> for Token<'a> {
fn from((value, lexeme): (ConstantKind, &'a str)) -> Self {
match value {
ConstantKind::Integer => Token::IntegerConstant(lexeme),
ConstantKind::BinInteger => Token::IntegerBinConstant(lexeme),
ConstantKind::OctInteger => Token::IntegerOctConstant(lexeme),
ConstantKind::HexInteger => Token::IntegerHexConstant(lexeme),
ConstantKind::DotFloating => Token::DotFloatingConstant(lexeme),
ConstantKind::DotFloatingExp => Token::DotFloatingExpConstant(lexeme),
ConstantKind::FloatingExp => Token::FloatingExpConstant(lexeme),
ConstantKind::Floating => Token::FloatingConstant(lexeme),
ConstantKind::Char => Token::CharConstant(lexeme),
ConstantKind::String => Token::StringConstant(lexeme),
}
}
}
pub(crate) fn parse_constant(source: &mut Source) -> Result<ConstantKind> {
let constant = parse_constant_inner(source)?;
// char following a constant must not be id_continue
if source
.peek()
.map(|&c| is_things::is_id_continue(c))
.unwrap_or(false)
{
return Err(Error::NumericalConstantUnknownSuffix);
}
Ok(constant)
}
pub(crate) fn parse_string_or_char_constant(source: &mut Source) -> Result<ConstantKind> {
let quote = source
.next_if(|&c| c == '"' || c == '\'')
.ok_or(Error::InvalidToken)?;
let is_char = quote == '\'';
let mut escaped = false;
let mut closed = false;
while let Some(c) = source.next() {
if escaped {
// accept any escaped char
escaped = false;
continue;
}
if c == '\\' {
escaped = true;
continue;
}
if c == quote {
closed = true;
break;
}
}
if !closed {
return Err(Error::StringError("Unterminated string/char.".into()));
}
if is_char {
Ok(ConstantKind::Char)
} else {
Ok(ConstantKind::String)
}
}
/// returns `Ok(true)` if it was a doc comment (///)
pub(crate) fn parse_comment<'a>(source: &'a mut Source) -> Result<bool> {
if !(source.next() == Some('/') && source.next() == Some('/')) {
return Err(Error::InvalidToken);
}
let doc = source.next_if_eq(&'/').is_some();
eprintln!("doc comment: {doc}");
loop {
// take until new line
source
.take_while_inclusive(|&c| c != '\n')
.inspect(|c| eprintln!("skipping comment char: {c}"))
.for_each(drop);
let mut copy = source.clone();
// skip whitespaces after new line to find continuation of comment
(&mut copy)
.take_while_ref(|&c| {
eprintln!("Skipping whitespace: {c}");
is_things::is_whitespace(c) && c != '\n'
})
.for_each(drop);
if (copy.next() == Some('/')) && (copy.next() == Some('/')) {
match copy.next() {
None => break,
// docs end here, regular comment starts
Some('\n') if doc => break,
// this is a comment, so we can just take until this new line
Some('\n') if !doc => continue,
// continue doc comment
Some('/') if doc => {}
Some('/') if !doc => break,
Some(_) if doc => break,
// continue regular comment
Some(_) => {}
}
*source = copy;
} else {
break;
}
}
Ok(doc)
}
#[cfg(test)]
mod tests {
use crate::complex_tokens::parse_comment;
use super::*;
fn make_source(s: &'_ str) -> Source<'_> {
s.chars().peekable().into()
}
#[test]
fn parse_constant_number() {
assert_eq!(
parse_constant(&mut make_source("0x1A3F_u32")),
Ok(ConstantKind::HexInteger)
);
assert_eq!(
parse_constant(&mut make_source("13f32")),
Ok(ConstantKind::Floating)
);
assert_eq!(
parse_constant(&mut make_source("0b1011_0010i16")),
Ok(ConstantKind::BinInteger)
);
assert_eq!(
parse_constant(&mut make_source("0o755u8")),
Ok(ConstantKind::OctInteger)
);
assert_eq!(
parse_constant(&mut make_source("42i64")),
Ok(ConstantKind::Integer)
);
assert_eq!(
parse_constant(&mut make_source("3.14f64")),
Ok(ConstantKind::DotFloating)
);
assert_eq!(
parse_constant(&mut make_source("2.71828e0f32")),
Ok(ConstantKind::DotFloatingExp)
);
assert_eq!(
parse_constant(&mut make_source("22e23")),
Ok(ConstantKind::FloatingExp)
);
}
}

View file

@ -1,818 +0,0 @@
#![feature(slice_swap_unchecked, iter_collect_into, push_mut)]
pub mod is_things {
/// True if `c` is considered a whitespace according to Rust language definition.
/// See [Rust language reference](https://doc.rust-lang.org/reference/whitespace.html)
/// for definitions of these classes.
pub fn is_whitespace(c: char) -> bool {
// This is Pattern_White_Space.
//
// Note that this set is stable (ie, it doesn't change with different
// Unicode versions), so it's ok to just hard-code the values.
matches!(
c,
// Usual ASCII suspects
'\u{0009}' // \t
| '\u{000A}' // \n
| '\u{000B}' // vertical tab
| '\u{000C}' // form feed
| '\u{000D}' // \r
| '\u{0020}' // space
// NEXT LINE from latin1
| '\u{0085}'
// Bidi markers
| '\u{200E}' // LEFT-TO-RIGHT MARK
| '\u{200F}' // RIGHT-TO-LEFT MARK
// Dedicated whitespace characters from Unicode
| '\u{2028}' // LINE SEPARATOR
| '\u{2029}' // PARAGRAPH SEPARATOR
)
}
/// True if `c` is valid as a first character of an identifier.
/// See [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html) for
/// a formal definition of valid identifier name.
pub fn is_id_start(c: char) -> bool {
// This is XID_Start OR '_' (which formally is not a XID_Start).
c == '_' || c == '-' || unicode_xid::UnicodeXID::is_xid_start(c)
}
/// True if `c` is valid as a non-first character of an identifier.
/// See [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html) for
/// a formal definition of valid identifier name.
pub fn is_id_continue(c: char) -> bool {
unicode_xid::UnicodeXID::is_xid_continue(c) || c == '-'
}
/// The passed string is lexically an identifier.
pub fn is_ident(string: &str) -> bool {
let mut chars = string.chars();
if let Some(start) = chars.next() {
is_id_start(start) && chars.all(is_id_continue)
} else {
false
}
}
pub fn is_digit(ch: char) -> bool {
('0'..='9').contains(&ch)
}
pub fn is_bin_digit(ch: char) -> bool {
ch == '0' || ch == '1'
}
#[expect(dead_code)]
pub fn is_nonzero_digit(ch: char) -> bool {
('1'..='9').contains(&ch)
}
pub fn is_oct_digit(ch: char) -> bool {
('0'..='7').contains(&ch)
}
pub fn is_hex_digit(ch: char) -> bool {
('0'..='9').contains(&ch) || ('a'..='f').contains(&ch) || ('A'..='F').contains(&ch)
}
}
macro_rules! tokens {
($vis:vis $ty_name:ident:
{
$($(#[$meta2:meta])* $name2:ident),*
},
{
$($(#[$meta:meta])* $name:ident => $lexeme:literal),*
}) => {
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
$vis enum $ty_name<'a> {
$($(#[$meta])* $name,
)*
$($(#[$meta2])* $name2(&'a str),)*
}
impl ::core::fmt::Display for $ty_name<'_> {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
match self {
$(Self::$name => write!(f, "{}", $lexeme),)*
$(Self::$name2(lexeme) => write!(f, "[{}: {lexeme}]", stringify!($name2))),*
}
}
}
#[allow(dead_code)]
impl $ty_name<'_> {
$vis fn lexeme(&'_ self) -> &'_ str {
match self {
$(Self::$name => $lexeme,)*
$(Self::$name2(lexeme) => lexeme),*
}
}
/// returns the number of chars in this lexeme
$vis fn lexeme_len(&self) -> usize {
self.lexeme().chars().count()
}
/// returns the number of chars in this lexeme
$vis fn lexeme_len_utf8(&self) -> usize {
self.lexeme().len()
}
$vis fn maybe_ident(&self) -> bool {
crate::is_things::is_ident(self.lexeme())
}
$vis fn lexemes() -> &'static [(Token<'static>, &'static str)] {
&[
$((Token::$name, $lexeme)),*
]
}
}
};
}
tokens!(pub Token: {
Eof,
ParseError,
// Marker Token for any Comment
Comment,
DocComment,
/// character constant, e.g. `'a'` or `'\n'`
CharConstant,
/// Decimal integer constant, e.g. `12345`
IntegerConstant,
/// Hexadecimal integer constant with leading `0x`, e.g. `0x1A3F`
IntegerHexConstant,
/// Binary integer constant with leading `0b`, e.g. `0b10101`
IntegerBinConstant,
/// Octal integer constant with leading `0o`, e.g. `0o7654`
IntegerOctConstant,
/// Simple floating point constant, e.g. `1f32`
FloatingConstant,
/// Simple floating point constant with exponent, e.g. `2e10f64`
FloatingExpConstant,
/// Floating point constant starting with a dot, e.g. `.5f32`
DotFloatingConstant,
/// Floating point constant starting with a dot and with an exponent, e.g. `.5e-2f64`
DotFloatingExpConstant,
/// String constant, e.g. `"hello, world!"`
StringConstant,
/// Identifier
Ident
},
// Lexical Tokens:
{
// SlashSlash => "//",
// SlashSlashSlash => "///",
// SlashStar => "/*",
// SlashStarStar => "/**",
//StarSlash => "*/",
// Punctuation:
OpenParens => "(",
CloseParens => ")",
OpenBrace => "{",
CloseBrace => "}",
OpenSquareBracket => "[",
CloseSquareBracket => "]",
Semi => ";",
Comma => ",",
Elipsis3 => "...",
Elipsis2 => "..",
Colon => ":",
Equal => "=",
// Keywords:
True => "true",
False => "false",
Void => "void",
Bool => "bool",
F32 => "f32",
F64 => "f64",
ISize => "isize",
USize => "usize",
U1 => "u1",
U8 => "u8",
U16 => "u16",
U32 => "u32",
U64 => "u64",
I1 => "i1",
I8 => "i8",
I16 => "i16",
I32 => "i32",
I64 => "i64",
Const => "const",
Mutable => "mut",
Volatile => "volatile",
Noalias => "noalias",
Fn => "fn",
Let => "let",
Var => "var",
If => "if",
As => "as",
Else => "else",
Return => "return",
Struct => "struct",
Type => "type",
Union => "union",
Enum => "enum",
Packed => "packed",
Extern => "extern",
Pub => "pub",
Module => "mod",
// Operators
Dot => ".",
MinusGreater => "->",
Bang => "!",
Tilde => "~",
Plus => "+",
// PlusPlus => "++",
Minus => "-",
// MinusMinus => "--",
Star => "*",
Slash => "/",
Percent => "%",
Less => "<",
Greater => ">",
LessEqual => "<=",
GreaterEqual => ">=",
EqualEqual => "==",
BangEqual => "!=",
PipePipe => "||",
AmpersandAmpersand => "&&",
Ampersand => "&",
Caret => "^",
Pipe => "|",
LessLess => "<<",
GreaterGreater => ">>",
Question => "?",
PlusEqual => "+=",
MinusEqual => "-=",
StarEqual => "*=",
SlashEqual => "/=",
PercentEqual => "%=",
AmpersandEqual => "&=",
PipeEqual => "|=",
CaretEqual => "^=",
LessLessEqual => "<<=",
GreaterGreaterEqual => ">>="
});
impl Token<'_> {
pub fn is_assignment_op(self) -> bool {
match self {
Token::PlusEqual
| Token::MinusEqual
| Token::StarEqual
| Token::SlashEqual
| Token::PercentEqual
| Token::PipeEqual
| Token::CaretEqual
| Token::AmpersandEqual
| Token::LessLessEqual
| Token::GreaterGreaterEqual
| Token::Equal => true,
_ => false,
}
}
pub fn is_unary_op(self) -> bool {
match self {
Token::Plus | Token::Minus | Token::Star | Token::Ampersand | Token::Bang => true,
_ => false,
}
}
pub fn is_binary_op(self) -> bool {
match self {
Token::Star
| Token::Slash
| Token::Percent
| Token::Pipe
| Token::Ampersand
| Token::Caret
| Token::Plus
| Token::Minus
| Token::PipePipe
| Token::AmpersandAmpersand
| Token::BangEqual
| Token::EqualEqual
| Token::Less
| Token::Greater
| Token::LessEqual
| Token::GreaterEqual
| Token::LessLess
| Token::GreaterGreater => true,
_ => false,
}
}
}
use std::{marker::PhantomData, ops::Range};
use trie::Tree;
#[derive(Debug, Clone, Copy)]
pub struct TokenItem<'a> {
pub token: Token<'a>,
pub offset: u32,
}
#[derive(Debug, Clone, Copy)]
struct CharCountingIterator<I: Iterator> {
iter: I,
count: usize,
}
impl<I: Iterator> From<I> for CharCountingIterator<I> {
fn from(iter: I) -> Self {
Self { iter, count: 0 }
}
}
impl<I: Iterator<Item = char>> Iterator for CharCountingIterator<I> {
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().inspect(|c| self.count += c.len_utf8())
}
}
impl<I: Iterator> CharCountingIterator<I> {
pub(crate) fn offset(&self) -> usize {
self.count
}
}
impl<I: Iterator<Item = char>> CharCountingIterator<core::iter::Peekable<I>> {
fn peek(&mut self) -> Option<&I::Item> {
self.iter.peek()
}
fn next_if_eq(&mut self, expected: &I::Item) -> Option<I::Item>
where
I::Item: PartialEq,
{
self.iter
.next_if_eq(expected)
.inspect(|c| self.count += c.len_utf8())
}
}
// impl<I: Iterator> core::ops::Deref for CharCountingIterator<I> {
// type Target = I;
// fn deref(&self) -> &Self::Target {
// &self.iter
// }
// }
// impl<I: Iterator> core::ops::DerefMut for CharCountingIterator<I> {
// fn deref_mut(&mut self) -> &mut Self::Target {
// &mut self.iter
// }
// }
type Source<'a> = CharCountingIterator<core::iter::Peekable<core::str::Chars<'a>>>;
pub struct TokenIterator<'a> {
trie: Tree<char, Token<'static>>,
source: &'a str,
offset: usize,
}
impl<'a> TokenIterator<'a> {
pub fn new(source: &'a str) -> Self {
let mut trie = Tree::new();
for (token, token_str) in Token::lexemes() {
trie.insert(token_str.chars(), *token);
}
Self {
trie,
source,
offset: 0,
}
}
fn peekable_source(&self) -> Source<'a> {
CharCountingIterator::from(self.source[self.offset..].chars().peekable())
}
fn parse(&mut self) -> Option<Token<'static>> {
let mut iter = CharCountingIterator::from(self.source[self.offset..].chars());
match self.trie.get_closest(&mut iter) {
Some(token) => {
// skip the peeked item
self.offset += token.lexeme_len();
Some(*token)
}
None => None,
}
}
fn skip_whitespaces(&mut self) -> usize {
self.skip_while(is_things::is_whitespace)
}
fn skip(&mut self, mut n: usize) -> usize {
self.skip_while(|_| {
n -= 1;
n > 0
})
}
fn skip_while(&mut self, mut pred: impl FnMut(char) -> bool) -> usize {
let mut count = 0;
loop {
let Some(c) = self.source[self.offset..].chars().next() else {
break;
};
if pred(c) {
self.offset += c.len_utf8();
count += c.len_utf8();
continue;
} else {
break;
}
}
count
}
fn follows(&self, s: &str) -> bool {
self.source[self.offset..].starts_with(s)
}
fn next_token(&mut self) -> Option<(Token<'a>, Range<usize>)> {
// skip whitespace
self.skip_whitespaces();
let start = self.offset;
let mut source = self.peekable_source();
let mut cursor = self.peekable_source();
let token = match cursor.next() {
Some('0'..='9') => {
let token = complex_tokens::parse_constant(&mut source).ok()?;
self.offset += source.offset();
Some((token, &self.source[start..self.offset]).into())
}
Some('.') if cursor.next().map_or(false, is_things::is_digit) => {
let token = complex_tokens::parse_constant(&mut source).ok()?;
self.offset += source.offset();
Some((token, &self.source[start..self.offset]).into())
}
Some('\'' | '"') => {
let token = complex_tokens::parse_string_or_char_constant(&mut source).ok()?;
self.offset += source.offset();
Some((token, &self.source[start..self.offset]).into())
}
Some('`') => {
// raw identifier
self.skip(1);
self.skip_while(|c| is_things::is_id_continue(c));
if self.peekable_source().next() == Some('`') {
self.skip(1);
let lexeme = &self.source[start..self.offset];
Some(Token::Ident(lexeme))
} else {
// unterminated raw identifier
let lexeme = &self.source[start..self.offset];
Some(Token::ParseError(lexeme))
}
}
// `//`-style comments or doc-comments
Some('/') if self.follows("//") => {
let doc = complex_tokens::parse_comment(&mut source).ok()?;
self.offset += source.offset();
eprintln!("next: {:?}", source.next());
eprintln!("rest: {:?}", &self.source[self.offset..]);
let lexeme = &self.source[start..self.offset];
if doc {
Some(Token::DocComment(lexeme))
} else {
Some(Token::Comment(lexeme))
}
}
_ => match self.parse() {
Some(tok) => {
if tok.maybe_ident() && self.skip_while(|c| is_things::is_id_continue(c)) > 0 {
Some(Token::Ident(&self.source[start..self.offset]))
} else {
Some(tok)
}
}
None => {
if self
.peekable_source()
.next()
.map_or(false, |c| is_things::is_id_start(c))
{
self.skip(1);
self.skip_while(|c| is_things::is_id_continue(c));
Some(Token::Ident(&self.source[start..self.offset]))
} else {
None
}
}
},
}?;
Some((token, start..self.offset))
}
fn next_token_item(&mut self) -> Option<TokenItem<'a>> {
let (token, range) = self.next_token()?;
Some(TokenItem {
token,
offset: range.start as u32,
})
}
pub fn into_token_items(self) -> TokenItemIterator<'a> {
TokenItemIterator { inner: self }
}
}
impl<'a> Iterator for TokenIterator<'a> {
type Item = Token<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.next_token().map(|(token, _)| token)
}
}
pub struct TokenItemIterator<'a> {
inner: TokenIterator<'a>,
}
impl<'a> Iterator for TokenItemIterator<'a> {
type Item = TokenItem<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next_token_item()
}
}
pub trait TokenConsumer<'a> {
type Product;
type Error;
fn try_consume_tokens<I: Iterator<Item = TokenItem<'a>> + Clone>(
&mut self,
iter: &mut I,
) -> Result<Self::Product, Self::Error>;
}
struct SimpleTokenConsumer<S, T: Default = ()>(S, PhantomData<T>);
impl<'a, S, T> TokenConsumer<'a> for SimpleTokenConsumer<S, T>
where
S: TokenSequence,
T: Default,
{
type Product = T;
type Error = ();
fn try_consume_tokens<I: Iterator<Item = TokenItem<'a>> + Clone>(
&mut self,
iter: &mut I,
) -> Result<Self::Product, Self::Error> {
let ref mut iter2 = iter.clone();
if iter2
.zip(self.0.tokens().iter().copied())
.all(|(item, expected)| item.token == expected)
{
core::mem::swap(iter, iter2);
Ok(T::default())
} else {
Err(())
}
}
}
struct TokenSequenceListConsumer<L: TokenSequenceList> {
list: L,
}
impl<'a, L: TokenSequenceList> TokenConsumer<'a> for TokenSequenceListConsumer<L> {
type Product = Vec<TokenItem<'a>>;
type Error = ();
fn try_consume_tokens<I: Iterator<Item = TokenItem<'a>> + Clone>(
&mut self,
iter: &mut I,
) -> Result<Self::Product, Self::Error> {
let sequences = self.list.iter_sequences();
for seq in sequences {
let mut iter2 = StealingIterator {
iter: iter.clone(),
yielded: Vec::new(),
};
if (&mut iter2)
.zip(seq.iter().copied())
.all(|(item, expected)| item.token == expected)
{
core::mem::swap(iter, &mut iter2.iter);
return Ok(iter2.yielded);
}
}
Err(())
}
}
struct StealingIterator<T, I: Iterator<Item = T>> {
pub iter: I,
pub yielded: Vec<T>,
}
impl<I, T> Iterator for StealingIterator<T, I>
where
T: Clone,
I: Iterator<Item = T>,
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(move |item| {
self.yielded.push(item.clone());
item
})
}
}
pub trait TokenSequence {
fn tokens(&'_ self) -> &'_ [Token<'_>];
}
impl TokenSequence for Token<'_> {
fn tokens(&'_ self) -> &'_ [Token<'_>] {
std::slice::from_ref(self)
}
}
impl TokenSequence for [Token<'_>] {
fn tokens(&'_ self) -> &'_ [Token<'_>] {
self
}
}
impl TokenSequence for &[Token<'_>] {
fn tokens(&'_ self) -> &'_ [Token<'_>] {
self
}
}
impl<const N: usize> TokenSequence for [Token<'_>; N] {
fn tokens(&'_ self) -> &'_ [Token<'_>] {
self
}
}
pub trait TokenSequenceList {
fn for_each(&mut self, f: impl FnMut(&dyn TokenSequence));
fn iter_sequences(&'_ self) -> impl Iterator<Item = &[Token<'_>]>;
fn first<T>(&mut self, pred: impl FnMut(&dyn TokenSequence) -> Option<T>) -> Option<T>;
}
impl<T: TokenSequence> TokenSequenceList for T {
fn for_each(&mut self, mut f: impl FnMut(&dyn TokenSequence)) {
f(self);
}
fn iter_sequences(&'_ self) -> impl Iterator<Item = &[Token<'_>]> {
std::iter::once(self.tokens())
}
fn first<U>(&mut self, mut pred: impl FnMut(&dyn TokenSequence) -> Option<U>) -> Option<U> {
pred(self)
}
}
macro_rules! impl_token_sequence_list {
($(($is:tt, $ts:ident)),*) => {
impl<$($ts,)*> $crate::TokenSequenceList for ($($ts,)*) where
$($ts: $crate::TokenSequenceList,)* {
fn for_each(&mut self, mut f: impl FnMut(&dyn $crate::TokenSequence)) {
$(self.$is.for_each(&mut f);)*
}
fn iter_sequences(&'_ self) -> impl Iterator<Item = &[Token<'_>]> {
std::iter::empty()
$(.chain(self.$is.iter_sequences()))*
}
fn first<U>(&mut self, mut pred: impl FnMut(&dyn $crate::TokenSequence) -> Option<U>) -> Option<U> {
$(
if let Some(res) = self.$is.first(&mut pred) {
return Some(res);
}
)*
None
}
}
};
}
variadics_please::all_tuples_enumerated!(impl_token_sequence_list, 1, 15, T);
mod complex_tokens;
pub use complex_tokens::Radix;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_iterator() {
let tokens = "fn let void+(+bool)";
let mut lexer = TokenIterator::new(&tokens);
assert_eq!(lexer.next(), Some(Token::Fn));
assert_eq!(lexer.next(), Some(Token::Let));
assert_eq!(lexer.next(), Some(Token::Void));
assert_eq!(lexer.next(), Some(Token::Plus));
assert_eq!(lexer.next(), Some(Token::OpenParens));
assert_eq!(lexer.next(), Some(Token::Plus));
assert_eq!(lexer.next(), Some(Token::Bool));
assert_eq!(lexer.next(), Some(Token::CloseParens));
assert_eq!(lexer.next(), None);
}
#[test]
fn idents() {
let mut lexer = TokenIterator::new("a a1 a_ a-b _a _1 _- -a -1 -_ `123");
assert!(lexer.all(|tok| matches!(tok, Token::Ident(_))));
}
#[test]
fn ident_minus_ambiguity() {
let lexer = TokenIterator::new("a-a a- - a -a --a");
let tokens = lexer.collect::<Vec<_>>();
assert_eq!(
tokens,
vec![
Token::Ident("a-a"),
Token::Ident("a-"),
Token::Minus,
Token::Ident("a"),
Token::Ident("-a"),
Token::Ident("--a")
]
);
}
#[test]
fn comments() {
let mut lexer = TokenIterator::new(
r#"
// this is a comment
// spanning two lines
/// this is a doc comment"#,
);
assert_eq!(
lexer.next(),
Some(Token::Comment(
"// this is a comment\n// spanning two lines\n"
))
);
assert_eq!(
lexer.next(),
Some(Token::DocComment("/// this is a doc comment"))
);
}
#[test]
fn complex_iterator() {
let tokens = "fn my-function(x: i32, y: f32) -> f32 { return x + y; }";
let lexer = TokenIterator::new(&tokens);
let mut items = lexer.into_token_items().map(|item| item.token);
assert_eq!(items.next(), Some(Token::Fn));
assert_eq!(items.next(), Some(Token::Ident("my-function")));
assert_eq!(items.next(), Some(Token::OpenParens));
assert_eq!(items.next(), Some(Token::Ident("x")));
assert_eq!(items.next(), Some(Token::Colon));
assert_eq!(items.next(), Some(Token::I32));
assert_eq!(items.next(), Some(Token::Comma));
assert_eq!(items.next(), Some(Token::Ident("y")));
assert_eq!(items.next(), Some(Token::Colon));
assert_eq!(items.next(), Some(Token::F32));
assert_eq!(items.next(), Some(Token::CloseParens));
assert_eq!(items.next(), Some(Token::MinusGreater));
assert_eq!(items.next(), Some(Token::F32));
assert_eq!(items.next(), Some(Token::OpenBrace));
assert_eq!(items.next(), Some(Token::Return));
assert_eq!(items.next(), Some(Token::Ident("x")));
assert_eq!(items.next(), Some(Token::Plus));
assert_eq!(items.next(), Some(Token::Ident("y")));
assert_eq!(items.next(), Some(Token::Semi));
assert_eq!(items.next(), Some(Token::CloseBrace));
assert_eq!(items.next(), None);
}
}

View file

@ -1,17 +0,0 @@
[package]
name = "parser"
version = "0.1.0"
edition = "2024"
[dependencies]
tracing = { workspace = true }
werkzeug = { workspace = true }
thiserror = { workspace = true }
itertools = { workspace = true }
internment = "0.8.6"
lexer = { path = "../lexer", version = "0.1.0" }
logos = "0.15"
pomelo = "0.2"

View file

@ -1,104 +0,0 @@
use internment::Intern;
use itertools::Itertools;
use lexer::{Radix, Token, is_things};
use werkzeug::iter::NextIf;
use crate::{FloatType, InnerType, IntSize, Type, Value};
pub(crate) fn parse_floating_constant(lexeme: &str) -> (Intern<Value>, Type) {
let (value, ty) = lexeme
.strip_suffix("f32")
.map(|l| (Value::F32(l.parse().unwrap()), FloatType::F32))
.or_else(|| {
lexeme
.strip_suffix("f64")
.map(|l| (Value::F64(l.parse().unwrap()), FloatType::F64))
})
.unwrap_or((Value::F32(lexeme.parse().unwrap()), FloatType::F32));
(
Intern::new(value),
Intern::new(InnerType::Float { float_type: ty }),
)
}
pub(crate) fn parse_constant(token: Token<'_>) -> (Intern<Value>, Type) {
let lexeme = match token {
Token::FloatingConstant(lexeme)
| Token::DotFloatingConstant(lexeme)
| Token::FloatingExpConstant(lexeme)
| Token::DotFloatingExpConstant(lexeme) => parse_floating_constant(lexeme),
Token::IntegerConstant(lexeme) => parse_integer_constant(lexeme, Radix::Dec),
Token::IntegerHexConstant(lexeme)
| Token::IntegerOctConstant(lexeme)
| Token::IntegerBinConstant(lexeme) => {
let radix = Radix::from_token(token).unwrap();
parse_integer_constant(&lexeme[2..], radix)
}
_ => unreachable!(),
};
lexeme
}
pub(crate) fn parse_integer_constant(lexeme: &str, radix: Radix) -> (Intern<Value>, Type) {
let mut chars = lexeme.char_indices();
let digits = chars.take_while_ref(|&(_, c)| radix.is_digit()(c) && c != '_');
let value = digits
.map(|(_, c)| radix.map_digit(c))
.fold(0u64, |acc, d| acc * radix.radix() as u64 + d as u64);
let value = Intern::new(Value::UInt(value));
let ty = chars
.clone()
.next_if(|&(_, c)| c == 'u' || c == 'i')
// integral type and signed-ness
.map(|(i, c)| (&lexeme[(i + 1)..], c == 'i'))
.map(|(bits, signed)| {
let mut chars = bits.chars();
let mut bits = 0u16;
let x = 'f: {
while let Some(c) = chars.next() {
if !is_things::is_digit(c) {
break 'f None;
}
// TODO: check overflow
bits = bits * 10 + Radix::Dec.map_digit(c) as u16;
}
Some(bits)
};
// TODO: error out on invalid type
x.map(|bits| InnerType::Int {
signed,
size: IntSize::Bits(bits),
})
})
.flatten()
.unwrap_or(InnerType::AnyInt);
(value, Intern::new(ty))
}
pub(crate) fn type_from_value(value: &Value) -> Type {
let inner = match value {
Value::F32(_) => InnerType::Float {
float_type: FloatType::F32,
},
Value::F64(_) => InnerType::Float {
float_type: FloatType::F64,
},
Value::Bool(_) => InnerType::Bool,
Value::Int(_) => InnerType::AnyInt,
Value::UInt(_) => InnerType::AnyUInt,
Value::String(_) => InnerType::Str,
Value::Unit => InnerType::Unit,
};
Intern::new(inner)
}

View file

@ -1,665 +0,0 @@
use std::hash::Hash;
use internment::Intern;
use lexer::{Token, TokenConsumer, TokenItem, TokenItemIterator};
use logos::Logos;
use pomelo::pomelo;
use thiserror::Error;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum IntSize {
Bits(u16),
Pointer,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum InnerType {
Top,
Bottom,
Unit,
Bool,
/// A signed integer constant; concrete type undetermined
AnyInt,
/// An unsigned integer constant; concrete type undetermined
AnyUInt,
/// A string slice
Str,
Int {
signed: bool,
size: IntSize,
},
Float {
float_type: FloatType,
},
Pointer {
pointee: Box<Type>,
},
Array {
element: Box<Type>,
size: usize,
},
Function {
return_type: Box<Type>,
parameter_types: Vec<Type>,
},
Tuple {
elements: Vec<Type>,
},
TypeUnion {
types: Vec<Type>,
},
TypeIntersection {
types: Vec<Type>,
},
}
type Type = internment::Intern<InnerType>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FloatType {
F32,
F64,
}
#[derive(Debug, PartialEq, Clone)]
pub enum Value {
Bool(bool),
Int(i64),
UInt(u64),
F64(f64),
F32(f32),
String(String),
Unit,
}
impl Eq for Value {}
impl Hash for Value {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
core::mem::discriminant(self).hash(state);
match self {
Value::Bool(b) => b.hash(state),
Value::Int(i) => i.hash(state),
Value::UInt(u) => u.hash(state),
Value::F64(f) => {
werkzeug::util::hash_f64(state, f);
}
Value::F32(f) => {
werkzeug::util::hash_f32(state, f);
}
Value::String(s) => s.hash(state),
Value::Unit => {}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ControlFlowKind {
Return,
Break,
Continue,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Index(u32);
#[derive(Debug)]
pub enum AstNode {
Root {
files: Vec<Index>,
},
File {
decls: Vec<Index>,
},
FunctionProto {
name: String,
return_type: Type,
parameter_list: Index,
},
ParameterList {
parameters: Vec<Index>,
},
Parameter {
name: String,
param_type: Type,
},
FunctionDecl(FunctionDecl),
Block {
statements: Vec<Index>,
expr: Option<Index>,
},
Constant {
ty: Type,
value: Intern<Value>,
},
NoopExpr,
Stmt {
expr: Index,
},
ControlFlow {
kind: ControlFlowKind,
expr: Option<Index>,
},
VarDecl {
mutable: bool,
name: String,
var_type: Type,
},
Assignment {
dest: Index,
expr: Index,
},
GlobalDecl {
name: String,
var_type: Type,
value: Index,
},
StructDecl {
name: String,
fields: Vec<Index>,
},
FieldDecl {
name: String,
field_type: Type,
},
FieldAccess {
expr: Index,
field: String,
},
UnresolvedDeclRef {
name: String,
},
DeclRef {
decl: Index,
},
TypeDeclRef {
ty: Index,
},
ExplicitCast {
expr: Index,
ty: Type,
},
Deref {
expr: Index,
},
AddressOf {
expr: Index,
},
PlaceToValue {
expr: Index,
},
ValueToPlace {
expr: Index,
},
CallExpr {
callee: Index,
arguments: Vec<Index>,
},
Argument {
expr: Index,
},
Not(Index),
Negate(Index),
Multiply {
left: Index,
right: Index,
},
Divide {
left: Index,
right: Index,
},
Modulus {
left: Index,
right: Index,
},
Add {
left: Index,
right: Index,
},
Subtract {
left: Index,
right: Index,
},
BitOr {
left: Index,
right: Index,
},
BitAnd {
left: Index,
right: Index,
},
BitXor {
left: Index,
right: Index,
},
LogicalOr {
left: Index,
right: Index,
},
LogicalAnd {
left: Index,
right: Index,
},
Eq {
left: Index,
right: Index,
},
NotEq {
left: Index,
right: Index,
},
Less {
left: Index,
right: Index,
},
LessEq {
left: Index,
right: Index,
},
Greater {
left: Index,
right: Index,
},
GreaterEq {
left: Index,
right: Index,
},
ShiftLeft {
left: Index,
right: Index,
},
ShiftRight {
left: Index,
right: Index,
},
Subscript {
expr: Index,
index: Index,
},
If {
condition: Index,
then: Index,
r#else: Option<Index>,
},
Else {
expr: Index,
},
Comment {
text: String,
},
Attributes {
attrs: Vec<Index>,
},
Doc {
text: String,
},
Error {
err: Box<dyn core::error::Error>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Visibility {
#[default]
Private,
Public,
}
#[derive(Debug, Error)]
pub enum ParseError<'a> {
#[error("End of file.")]
EOF,
#[error("Unexpected token: {0:?}")]
UnexpectedToken(Token<'a>),
#[error("Not a type.")]
NotAType,
}
#[derive(Default, Debug)]
pub struct Ast {
nodes: Vec<AstNode>,
}
impl Ast {
pub fn new() -> Self {
Self::default()
}
pub fn push(&mut self, node: AstNode) -> Index {
let index = self.nodes.len() as u32;
self.nodes.push(node);
Index(index)
}
}
#[derive(Debug)]
struct FunctionDecl {
attrs: Option<Index>,
name: String,
visibility: Visibility,
return_type: Type,
parameter_list: Option<ParameterList>,
body: Index,
}
#[derive(Debug)]
struct Parameter {
mutable: bool,
name: String,
param_type: Type,
}
#[derive(Debug)]
struct ParameterList {
parameters: Vec<Index>,
}
#[derive(Debug)]
struct ExtraToken<'a> {
lexeme: &'a str,
offset: u32,
}
pomelo! {
%include {
use super::AstNode;
use internment::Intern;
use super::{
Parameter, Ast, ParameterList, FunctionDecl, Type, InnerType,
FloatType, ExtraToken, Index, IntSize, Visibility, Value,
};
};
%extra_argument Ast;
%parser pub struct Parser<'a>{};
%token #[derive(Debug)] pub enum Token<'a> {};
%type Ident &'a str;
%type DocComment &'a str;
%type Comment &'a str;
%type fn_decl FunctionDecl;
%type parameter Parameter;
%type parameter_list ParameterList;
%type typ Type;
%type return_type Type;
%type block Index;
%type decl Index;
%type decl_list Vec<Index>;
%type file Index;
file ::= decl_list?(list) {
let decls = list.unwrap_or_default();
extra.push(AstNode::File { decls })
};
decl_list ::= decl(decl) { vec![decl] };
decl_list ::= decl_list(dl) decl(decl) {
let mut list = dl;
list.push(decl);
list
};
%type attrs Index;
attrs ::= DocComment(text) {
let idx = extra.push(AstNode::Doc { text: text.to_string() });
extra.push(AstNode::Attributes { attrs: vec![idx] })
};
typ ::= Bool { Intern::new(InnerType::Bool) };
typ ::= I1 { Intern::new(InnerType::Int { signed: true, size: IntSize::Bits(1) }) };
typ ::= I8 { Intern::new(InnerType::Int { signed: true, size: IntSize::Bits(8) }) };
typ ::= I16 { Intern::new(InnerType::Int { signed: true, size: IntSize::Bits(16) }) };
typ ::= I32 { Intern::new(InnerType::Int { signed: true, size: IntSize::Bits(32) }) };
typ ::= I64 { Intern::new(InnerType::Int { signed: true, size: IntSize::Bits(64) }) };
typ ::= U1 { Intern::new(InnerType::Int { signed: false, size: IntSize::Bits(1) }) };
typ ::= U8 { Intern::new(InnerType::Int { signed: false, size: IntSize::Bits(8) }) };
typ ::= U16 { Intern::new(InnerType::Int { signed: false, size: IntSize::Bits(16) }) };
typ ::= U32 { Intern::new(InnerType::Int { signed: false, size: IntSize::Bits(32) }) };
typ ::= U64 { Intern::new(InnerType::Int { signed: false, size: IntSize::Bits(64) }) };
typ ::= ISize { Intern::new(InnerType::Int { signed: true, size: IntSize::Pointer }) };
typ ::= USize { Intern::new(InnerType::Int { signed: false, size: IntSize::Pointer }) };
typ ::= F32 { Intern::new(InnerType::Float { float_type: FloatType::F32 }) };
typ ::= F64 { Intern::new(InnerType::Float { float_type: FloatType::F64 }) };
typ ::= Bang { Intern::new(InnerType::Bottom) };
typ ::= unit { Intern::new(InnerType::Unit) };
typ ::= Void { Intern::new(InnerType::Unit) };
unit ::= LParen RParen;
%type immediate (Intern<Value>, Type);
immediate ::= unit { (Intern::new(Value::Unit), Intern::new(InnerType::Unit)) };
immediate ::= False { (Intern::new(Value::Bool(false)), Intern::new(InnerType::Bool)) };
immediate ::= True { (Intern::new(Value::Bool(true)), Intern::new(InnerType::Bool)) };
%type Constant lexer::Token<'a>;
immediate ::= Constant(token) {
crate::constants::parse_constant(token)
};
%type expr Index;
%type stmt Index;
%type stmts Vec<Index>;
expr ::= immediate((value, ty)) {
extra.push(AstNode::Constant { ty, value })
};
stmt ::= Semi { extra.push(AstNode::NoopExpr) };
stmt ::= Comment(text) { extra.push(AstNode::Comment { text: text.to_string() }) };
stmt ::= expr(expr) Semi { extra.push(AstNode::Stmt { expr }) };
stmts ::= stmt(s) { vec![s] };
stmts ::= stmts(ss) stmt(s) {
let mut v = ss;
v.push(s);
v
};
%type block_inner (Vec<Index>, Option<Index>);
block_inner ::= {(vec![], None)};
block_inner ::= expr(expr) {(vec![], Some(expr))};
block_inner ::= stmts(ss) {(ss, None)};
block_inner ::= stmts(ss) expr(expr) {(ss, Some(expr))};
block ::= LBrace block_inner((ss, expr)) RBrace {
extra.push(AstNode::Block {
statements: ss,
expr
})
};
%type vis Visibility;
vis ::= Pub { Visibility::Public };
%type mutable bool;
mutable ::= Mutable { true };
mutable ::= { false };
return_type ::= Arrow typ(return_type) { return_type };
parameter ::= mutable(mutable) Ident(name) Colon typ(param_type) {
Parameter { mutable, name: name.to_string(), param_type }
};
parameter_list ::= parameter(p) {
let idx = extra.push(AstNode::Parameter { name: p.name, param_type: p.param_type });
ParameterList { parameters: vec![idx] }
};
parameter_list ::= parameter_list(pl) Comma parameter(p) {
let idx = extra.push(AstNode::Parameter { name: p.name, param_type: p.param_type });
let mut parameters = pl.parameters;
parameters.push(idx);
ParameterList { parameters }
};
parameter_list ::= parameter_list(pl) Comma {
pl
};
decl ::= Comment(text) { extra.push(AstNode::Comment { text: text.to_string() }) };
decl ::= fn_decl(f) { extra.push(AstNode::FunctionDecl(f)) };
fn_decl ::= attrs?(attrs) vis?(visibility) Fn Ident(name) LParen parameter_list?(parameters) RParen return_type(rtype) block(body) {
let name = name.to_string();
FunctionDecl {
attrs,
name,
visibility: visibility.unwrap_or_default(),
return_type: rtype,
parameter_list: parameters,
body,
}
};
}
impl<'a> From<lexer::Token<'a>> for parser::Token<'a> {
fn from(value: lexer::Token<'a>) -> Self {
use lexer::Token;
match value {
Token::Fn => Self::Fn,
Token::OpenParens => Self::LParen,
Token::CloseParens => Self::RParen,
Token::OpenBrace => Self::LBrace,
Token::CloseBrace => Self::RBrace,
Token::Ident(ident) => Self::Ident(ident),
Token::Comment(text) => Self::Comment(text),
Token::DocComment(text) => Self::DocComment(text),
Token::OpenSquareBracket => todo!(), // Self::LBracket,
Token::CloseSquareBracket => todo!(), // Self::RBracket,
Token::Comma => Self::Comma,
Token::Colon => Self::Colon,
Token::Semi => Self::Semi,
Token::Elipsis3 => todo!(),
Token::Elipsis2 => todo!(),
Token::Equal => todo!(),
Token::Void => Self::Void,
Token::Bool => Self::Bool,
Token::F32 => Self::F32,
Token::F64 => Self::F64,
Token::ISize => Self::ISize,
Token::USize => Self::USize,
Token::U1 => Self::U1,
Token::U8 => Self::U8,
Token::U16 => Self::U16,
Token::U32 => Self::U32,
Token::U64 => Self::U64,
Token::I1 => Self::I1,
Token::I8 => Self::I8,
Token::I16 => Self::I16,
Token::I32 => Self::I32,
Token::I64 => Self::I64,
Token::True => Self::True,
Token::False => Self::False,
Token::Const => todo!(), // Self::Const,
Token::Mutable => Self::Mutable,
Token::Volatile => todo!(),
Token::Noalias => todo!(),
Token::Let => todo!(),
Token::Var => todo!(),
Token::If => todo!(),
Token::As => todo!(),
Token::Else => todo!(),
Token::Return => todo!(),
Token::Struct => todo!(),
Token::Type => todo!(),
Token::Union => todo!(),
Token::Enum => todo!(),
Token::Packed => todo!(),
Token::Extern => todo!(),
Token::Pub => Self::Pub,
Token::Module => todo!(),
Token::Dot => todo!(),
Token::MinusGreater => Self::Arrow,
Token::Bang => Self::Bang,
Token::Tilde => todo!(),
Token::Plus => todo!(),
Token::Minus => todo!(),
Token::Star => todo!(),
Token::Slash => todo!(),
Token::Percent => todo!(),
Token::Less => todo!(),
Token::Greater => todo!(),
Token::LessEqual => todo!(),
Token::GreaterEqual => todo!(),
Token::EqualEqual => todo!(),
Token::BangEqual => todo!(),
Token::PipePipe => todo!(),
Token::AmpersandAmpersand => todo!(),
Token::Ampersand => todo!(),
Token::Caret => todo!(),
Token::Pipe => todo!(),
Token::LessLess => todo!(),
Token::GreaterGreater => todo!(),
Token::Question => todo!(),
Token::PlusEqual => todo!(),
Token::MinusEqual => todo!(),
Token::StarEqual => todo!(),
Token::SlashEqual => todo!(),
Token::PercentEqual => todo!(),
Token::AmpersandEqual => todo!(),
Token::PipeEqual => todo!(),
Token::CaretEqual => todo!(),
Token::LessLessEqual => todo!(),
Token::GreaterGreaterEqual => todo!(),
Token::Eof(_) => todo!(),
Token::ParseError(_) => todo!(),
Token::CharConstant(_) => todo!(),
Token::IntegerConstant(_) => Self::Constant(value),
Token::IntegerHexConstant(_) => Self::Constant(value),
Token::IntegerBinConstant(_) => Self::Constant(value),
Token::IntegerOctConstant(_) => Self::Constant(value),
Token::FloatingConstant(_) => Self::Constant(value),
Token::FloatingExpConstant(_) => Self::Constant(value),
Token::DotFloatingConstant(_) => Self::Constant(value),
Token::DotFloatingExpConstant(_) => Self::Constant(value),
Token::StringConstant(_) => todo!(),
}
}
}
mod constants;
#[cfg(test)]
mod tests {
use crate::AstNode;
#[test]
fn print_ast_node_size() {
eprintln!("Size of AstNode: {}", std::mem::size_of::<AstNode>());
}
#[test]
fn parse_constant() {
use crate::parser::{Parser, Token};
let input = r#"
fn a() -> u32 {
42u32
}
fn b() -> u32 {
42i8
}
fn c() -> f32 {
42e4
}
"#;
let mut lex = lexer::TokenIterator::new(input);
let mut mapped = lex.map(Token::from);
let mut ast = crate::Ast::new();
let mut parser = Parser::new(ast);
while let Some(token) = mapped.next() {
parser.parse(token).unwrap();
}
let (out, ast) = parser.end_of_input().unwrap();
eprintln!("AST: {:#?}", ast);
}
#[test]
fn parse() {
use crate::parser::{Parser, Token};
let input = r#"
// A simple test case
/// A function that takes two u32 parameters and returns a u32
fn main(a: u32, b: u32) -> u32 {}
"#;
let mut lex = lexer::TokenIterator::new(input);
let mut mapped = lex.map(Token::from);
let mut ast = crate::Ast::new();
let mut parser = Parser::new(ast);
while let Some(token) = mapped.next() {
parser.parse(token).unwrap();
}
let (out, ast) = parser.end_of_input().unwrap();
eprintln!("AST: {:#?}", ast);
}
}

View file

@ -1,96 +0,0 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1757745802,
"narHash": "sha256-hLEO2TPj55KcUFUU1vgtHE9UEIOjRcH/4QbmfHNF820=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c23193b943c6c689d70ee98ce3128239ed9e32d1",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1744536153,
"narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"rust-overlays": "rust-overlays"
}
},
"rust-overlays": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1757989933,
"narHash": "sha256-9cpKYWWPCFhgwQTww8S94rTXgg8Q8ydFv9fXM6I8xQM=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "8249aa3442fb9b45e615a35f39eca2fe5510d7c3",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View file

@ -1,31 +0,0 @@
{
description = "A nix flake for nightly rust";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
rust-overlays.url = "github:oxalica/rust-overlay";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils, rust-overlays, ...}:
flake-utils.lib.eachDefaultSystem (system: let
overlays = [
(import rust-overlays)
];
pkgs = import nixpkgs {
inherit system overlays;
};
rust = pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.default.override {
extensions = ["rust-src" "rust-analyzer"];
targets = [ "x86_64-unknown-linux-gnu" ];
});
in with pkgs; {
devShells.default = pkgs.mkShell {
buildInputs = [
pkg-config
git
rust
];
};
});
}

View file

@ -1,204 +0,0 @@
//! Type checking for the AST.
//! This module implements a bi-unification type-checking algorithm to infer
//! types for each node in the AST.
// Visitor pattern has lots of unused arguments
#![allow(unused_variables)]
use std::collections::{HashMap, HashSet};
use crate::ast2::tag::{AstNode, AstNodeExt};
use super::{Ast, Index, intern, visitor::AstVisitorTrait};
type Id = u32;
trait TypeVariance {
type T;
type Opposite;
}
#[derive(Debug, Clone)]
enum TypeHead<T: TypeVariance> {
Real(intern::Index),
Function { args: Vec<T::T>, ret: T::Opposite },
}
/// Variance of a type parameter or constraint.
/// A function of type `A -> B` is covariant in `B` and contravariant in `A`.
/// This means that a type `T` may be substituted for `A` if `T` is a subtype of
/// `A`, that is, every `T` is also an `A`,
/// but a type `T` may only be substituted for `B` if `T` is a supertype of `B`,
/// that is, every `B` is also a `T`.
///
/// Namely, in a type system with `int` and `nat <: int`, for a function `f: int
/// -> int` in the expression `let u: int = 3; let t: nat = f(u);`, `u` may
/// safely be used as an argument to `f` because `nat <: int`, but `f(u`)` may
/// not be assigned to `t` because `int <: nat` is not true.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
enum Variance {
/// A Positive, or union relationship between types.
/// used in value-places.
Covariant,
/// A Negative, or intersection relationship between types.
/// used in use-places.
Contravariant,
}
#[derive(Debug, Clone, Copy)]
struct Value(Id);
#[derive(Debug, Clone, Copy)]
struct Use(Id);
impl TypeVariance for Value {
type T = Value;
type Opposite = Use;
}
impl TypeVariance for Use {
type T = Use;
type Opposite = Value;
}
/// Typechecking error.
#[derive(Debug, Clone, thiserror::Error)]
enum Error {
#[error("Unimplemented feature")]
Unimplemented,
#[error("{0}")]
StringError(String),
}
type Result<T> = std::result::Result<T, Error>;
struct Bindings {
next_id: Id,
inner: HashMap<super::Index, Id>,
bounds: HashSet<(Id, Id, Variance)>,
types: HashMap<Id, intern::Index>,
}
impl Bindings {
fn new() -> Self {
Bindings {
next_id: 1,
inner: HashMap::new(),
bounds: HashSet::new(),
types: HashMap::new(),
}
}
fn new_id(&mut self) -> Id {
let id = self.next_id;
self.next_id += 1;
id
}
fn get_or_create(&mut self, idx: super::Index) -> Id {
self.inner.get(&idx).copied().unwrap_or_else(|| {
let id = self.new_id();
self.inner.insert(idx, id);
id
})
}
/// retrieves the type Id for the given ast node.
fn get(&self, idx: super::Index) -> Option<Id> {
self.inner.get(&idx).copied()
}
/// inserts a proper type for `id`.
fn insert_type(&mut self, id: Id, ty: intern::Index) {
self.types.insert(id, ty);
}
}
struct TypeChecker<'a> {
pool: &'a mut intern::InternPool,
bindings: Bindings,
}
// Core
impl TypeChecker<'_> {
pub fn new(pool: &mut intern::InternPool) -> TypeChecker {
TypeChecker {
pool,
bindings: Bindings::new(),
}
}
fn var(&mut self) -> (Value, Use) {
todo!()
}
}
// Frontend
impl<'a> AstVisitorTrait<&'a Ast> for TypeChecker<'_> {
type Error = Error;
type Value = Value;
const UNIMPL: Self::Error = Error::Unimplemented;
fn visit_interned_type_impl(
&mut self,
ast: &'a Ast,
idx: Index,
intern: intern::Index,
) -> std::result::Result<Self::Value, Self::Error> {
let id = self.bindings.get_or_create(idx);
match self.pool.get_key(intern) {
intern::Key::SimpleType {
ty: intern::SimpleType::ComptimeInt,
} => {
// This is a type variable.
}
intern::Key::SimpleType { .. }
| intern::Key::PointerType { .. }
| intern::Key::ArrayType { .. }
| intern::Key::FunctionType { .. }
| intern::Key::StructType { .. } => {
// This is a real type.
self.bindings.insert_type(id, intern);
}
_ => unreachable!(),
}
Ok(Value(id))
}
fn visit_constant_impl(
&mut self,
ast: &'a Ast,
idx: Index,
ty: Index,
value: intern::Index,
) -> std::result::Result<Self::Value, Self::Error> {
// get type from the pool
let AstNode::InternedType { intern } = ast.get_ast_node(ty) else {
panic!(
"Expected an interned type node, got {:?}",
ast.get_ast_node(ty)
);
};
match self.pool.get_key(intern) {
intern::Key::SimpleType {
ty: intern::SimpleType::ComptimeInt,
} => {
// This is a type variable.
}
intern::Key::SimpleType { .. }
| intern::Key::PointerType { .. }
| intern::Key::ArrayType { .. }
| intern::Key::FunctionType { .. }
| intern::Key::StructType { .. } => {
// This is a real type.
}
_ => unreachable!(),
}
todo!()
}
}

View file

@ -1,159 +0,0 @@
use std::fmt::Display;
use crate::{lexer::SourceLocation, writeln_indented};
use super::{
intern::{self, InternPoolWrapper as InternPool},
visitor::AstVisitorTrait,
Ast, Children, ComptimeCache, Index, Tag, TypeCache,
};
struct AstRenderVisitor<'a, W: core::fmt::Write> {
syms: &'a crate::symbol_table::syms2::Symbols,
ip: &'a InternPool,
cache: TypeCache,
comptime_cache: ComptimeCache,
scopes: Vec<Index>,
w: &'a mut W,
}
impl<'a, W: core::fmt::Write> AstVisitorTrait<&'a Ast> for AstRenderVisitor<'_, W> {
type Error = core::fmt::Error;
type Value = ();
const UNIMPL: Self::Error = core::fmt::Error;
fn visit_any(&mut self, ast: &'a Ast, idx: Index) -> Result<Self::Value, Self::Error> {
let display = NodeDisplay::new(ast, self.ip, idx).with_indent(self.scopes.len() as u32 * 2);
match display.tag {
Tag::File
| Tag::FunctionDecl
| Tag::GlobalDecl
| Tag::Block
| Tag::BlockTrailingExpr => {
self.scopes.push(idx);
}
_ => {}
}
write!(self.w, "{display}")?;
for child in display.children.0 {
self.visit_any(ast, child)?;
}
match display.tag {
Tag::File
| Tag::FunctionDecl
| Tag::GlobalDecl
| Tag::Block
| Tag::BlockTrailingExpr => {
self.scopes.pop();
}
_ => {}
}
Ok(())
}
}
pub struct AstRenderer<'a> {
ast: &'a Ast,
#[allow(dead_code)]
syms: &'a crate::symbol_table::syms2::Symbols,
ip: &'a InternPool,
cache: TypeCache,
comptime_cache: ComptimeCache,
}
pub struct NodeDisplay {
node: Index,
tag: Tag,
loc: SourceLocation,
children: Children,
is_comptime: bool,
ty: intern::Index,
indent: u32,
}
impl NodeDisplay {
pub fn new(ast: &Ast, ip: &InternPool, node: Index) -> NodeDisplay {
let tag = ast.tags[node];
let loc = ast.source_locs[node];
let children = Children(ast.get_node_children(node));
let ty = ast.get_type_of_node(ip, &mut TypeCache::new(), node);
let is_comptime = ast.is_node_comptime_evaluable(&mut ComptimeCache::default(), node);
Self {
node,
tag,
loc,
children,
is_comptime,
ty,
indent: 0,
}
}
fn with_indent(self, indent: u32) -> Self {
Self { indent, ..self }
}
}
impl Display for NodeDisplay {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self {
node,
tag,
loc,
children,
is_comptime,
ty,
indent,
} = self;
writeln_indented!(
*indent,
f,
"{node} {}({ty}) = ({loc}) {tag:?} {children}",
if *is_comptime { "CONST " } else { "" }
)?;
Ok(())
}
}
impl<'a> AstRenderer<'a> {
pub fn new(
ast: &'a Ast,
ip: &'a InternPool,
syms: &'a crate::symbol_table::syms2::Symbols,
) -> Self {
Self {
ast,
syms,
ip,
cache: TypeCache::new(),
comptime_cache: ComptimeCache::default(),
}
}
pub(crate) fn render<W: core::fmt::Write>(&mut self, w: &mut W) -> core::fmt::Result {
let mut visitor = AstRenderVisitor {
syms: self.syms,
ip: self.ip,
cache: TypeCache::new(),
comptime_cache: ComptimeCache::default(),
scopes: Vec::new(),
w,
};
for idx in self.ast.get_root_file_indices() {
visitor.visit_any(self.ast, idx)?;
}
Ok(())
}
}

View file

@ -15,27 +15,18 @@ use crate::{
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum SimpleType {
F32,
F32 = 0,
F64,
Bool,
Void,
USize,
ISize,
ComptimeInt,
/// Top type: this is the supertype of all types, and any value can coerce into it.
/// Although Rust's `()` is not a top type, it can be thought of as a top type in some contexts.
Top,
/// Bottom type: this is the subtype of all types, and it can coerce into a value of any type.
/// Akin to Rust's `!`.
Bottom,
UInt(u16),
SInt(u16),
}
impl From<u32> for SimpleType {
fn from(value: u32) -> Self {
let [discriminant, bits] = *crate::common::u32_as_u16_slice(&value);
match discriminant {
impl From<u8> for SimpleType {
fn from(value: u8) -> Self {
match value {
0 => Self::F32,
1 => Self::F64,
2 => Self::Bool,
@ -43,49 +34,26 @@ impl From<u32> for SimpleType {
4 => Self::USize,
5 => Self::ISize,
6 => Self::ComptimeInt,
7 => Self::Top,
8 => Self::Bottom,
9 => Self::UInt(bits),
10 => Self::SInt(bits),
_ => panic!("{value} is not a simple type"),
}
}
}
impl From<SimpleType> for u32 {
fn from(value: SimpleType) -> Self {
match value {
SimpleType::F32 => crate::common::u32_from_u16_slice(&[0, 0]),
SimpleType::F64 => crate::common::u32_from_u16_slice(&[1, 0]),
SimpleType::Bool => crate::common::u32_from_u16_slice(&[2, 0]),
SimpleType::Void => crate::common::u32_from_u16_slice(&[3, 0]),
SimpleType::USize => crate::common::u32_from_u16_slice(&[4, 0]),
SimpleType::ISize => crate::common::u32_from_u16_slice(&[5, 0]),
SimpleType::ComptimeInt => crate::common::u32_from_u16_slice(&[6, 0]),
SimpleType::Top => crate::common::u32_from_u16_slice(&[7, 0]),
SimpleType::Bottom => crate::common::u32_from_u16_slice(&[8, 0]),
SimpleType::UInt(bits) => crate::common::u32_from_u16_slice(&[9, bits]),
SimpleType::SInt(bits) => crate::common::u32_from_u16_slice(&[10, bits]),
}
}
}
impl Display for SimpleType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let fmt: std::borrow::Cow<str> = match self {
SimpleType::F32 => "f32".into(),
SimpleType::F64 => "f64".into(),
SimpleType::Bool => "bool".into(),
SimpleType::Void => "void".into(),
SimpleType::USize => "usize".into(),
SimpleType::ISize => "isize".into(),
SimpleType::ComptimeInt => "comptime_int".into(),
SimpleType::Top => "".into(),
SimpleType::Bottom => "".into(),
SimpleType::UInt(bits) => format!("u{bits}").into(),
SimpleType::SInt(bits) => format!("i{bits}").into(),
};
write!(f, "{fmt}",)
write!(
f,
"{}",
match self {
SimpleType::F32 => "f32",
SimpleType::F64 => "f64",
SimpleType::Bool => "bool",
SimpleType::Void => "void",
SimpleType::USize => "usize",
SimpleType::ISize => "isize",
SimpleType::ComptimeInt => "comptime_int",
}
)
}
}
@ -102,6 +70,8 @@ pub enum Tag {
F64,
PositiveInt,
NegativeInt,
UIntType,
SIntType,
SimpleType,
PointerType,
ArrayType,
@ -110,9 +80,9 @@ pub enum Tag {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(super) struct Item {
pub(super) tag: Tag,
pub(super) index: u32,
struct Item {
tag: Tag,
index: u32,
}
#[derive(Debug, Clone, PartialEq)]
@ -148,6 +118,12 @@ pub enum Key<'a> {
NegativeInt {
bigint: BigInt,
},
UIntType {
bit_width: u16,
},
SIntType {
bit_width: u16,
},
SimpleType {
ty: SimpleType,
},
@ -194,6 +170,8 @@ impl Display for KeyDisplay<'_> {
Key::F64 { bits } => write!(f, "{bits}")?,
Key::PositiveInt { ref bigint } => write!(f, "{bigint}")?,
Key::NegativeInt { ref bigint } => write!(f, "{bigint}")?,
Key::UIntType { bit_width } => write!(f, "u{bit_width}")?,
Key::SIntType { bit_width } => write!(f, "i{bit_width}")?,
Key::SimpleType { ty } => write!(f, "{ty}")?,
Key::PointerType { pointee, flags } => {
write!(f, "*{flags}{}", self.ip.display_key(pointee))?
@ -252,6 +230,8 @@ impl Hash for Key<'_> {
Key::F64 { bits } => ordered_float::OrderedFloat(*bits).hash(state),
Key::PositiveInt { bigint } => bigint.hash(state),
Key::NegativeInt { bigint } => bigint.hash(state),
Key::UIntType { bit_width: bits } => bits.hash(state),
Key::SIntType { bit_width: bits } => bits.hash(state),
Key::SimpleType { ty } => ty.hash(state),
Key::PointerType { pointee, flags } => (pointee, flags).hash(state),
Key::ArrayType {
@ -422,73 +402,6 @@ impl Index {
}
}
pub struct InternPoolWrapper(core::cell::UnsafeCell<InternPool>);
impl InternPoolWrapper {
pub fn as_mut(&self) -> &mut InternPool {
unsafe { &mut *self.0.get() }
}
pub fn as_ref(&self) -> &InternPool {
unsafe { &*self.0.get() }
}
pub fn new() -> Self {
InternPool::new().into()
}
}
impl From<InternPool> for InternPoolWrapper {
fn from(value: InternPool) -> Self {
Self(core::cell::UnsafeCell::new(value))
}
}
impl core::ops::Deref for InternPoolWrapper {
type Target = InternPool;
fn deref(&self) -> &Self::Target {
unsafe { &*self.0.get() }
}
}
impl core::ops::DerefMut for InternPoolWrapper {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut()
}
}
impl AsRef<InternPool> for InternPoolWrapper {
fn as_ref(&self) -> &InternPool {
Self::as_ref(self)
}
}
impl AsRef<InternPool> for InternPool {
fn as_ref(&self) -> &InternPool {
self
}
}
// impl AsMut<InternPool> for InternPoolWrapper {
// fn as_mut(&mut self) -> &mut InternPool {
// Self::as_mut(self)
// }
// }
// impl AsMut<InternPool> for InternPool {
// fn as_mut(&mut self) -> &mut InternPool {
// self
// }
// }
impl core::fmt::Debug for InternPoolWrapper {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("InternPoolWrapper")
.field(self.as_ref())
.finish()
}
}
pub struct InternPool {
tags: Vec<Tag>,
indices: Vec<u32>,
@ -547,8 +460,6 @@ macro_rules! static_keys {
}
static_keys!(
TOP => Key::SimpleType {ty: SimpleType::Top,},
BOTTOM => Key::SimpleType {ty: SimpleType::Bottom,},
BOOL => Key::SimpleType {ty: SimpleType::Bool,},
F32 => Key::SimpleType {ty: SimpleType::F32,},
F64 => Key::SimpleType {ty: SimpleType::F64,},
@ -556,20 +467,20 @@ static_keys!(
ISIZE => Key::SimpleType {ty: SimpleType::ISize,},
VOID => Key::SimpleType {ty: SimpleType::Void,},
COMPTIME_INT => Key::SimpleType {ty: SimpleType::ComptimeInt,},
I0 => Key::SimpleType { ty: SimpleType::SInt(0) },
U0 => Key::SimpleType { ty: SimpleType::UInt(0) },
I1 => Key::SimpleType { ty: SimpleType::SInt(1) },
U1 => Key::SimpleType { ty: SimpleType::UInt(1) },
I8 => Key::SimpleType { ty: SimpleType::SInt(8) },
U8 => Key::SimpleType { ty: SimpleType::UInt(8) },
I16 => Key::SimpleType { ty: SimpleType::SInt(16) },
U16 => Key::SimpleType { ty: SimpleType::UInt(16) },
I32 => Key::SimpleType { ty: SimpleType::SInt(32) },
U32 => Key::SimpleType { ty: SimpleType::UInt(32) },
I64 => Key::SimpleType { ty: SimpleType::SInt(64) },
U64 => Key::SimpleType { ty: SimpleType::UInt(64) },
I128 => Key::SimpleType { ty: SimpleType::SInt(128) },
U128 => Key::SimpleType { ty: SimpleType::UInt(128) },
I1 => Key::SIntType { bit_width: 1 },
U1 => Key::UIntType { bit_width: 1 },
I0 => Key::SIntType { bit_width: 0 },
U0 => Key::UIntType { bit_width: 0 },
I8 => Key::SIntType { bit_width: 8 },
U8 => Key::UIntType { bit_width: 8 },
I16 => Key::SIntType { bit_width: 16 },
U16 => Key::UIntType { bit_width: 16 },
I32 => Key::SIntType { bit_width: 32 },
U32 => Key::UIntType { bit_width: 32 },
I64 => Key::SIntType { bit_width: 64 },
U64 => Key::UIntType { bit_width: 64 },
I128 => Key::SIntType { bit_width: 128 },
U128 => Key::UIntType { bit_width: 128 },
TRUE => Key::TrueValue,
FALSE => Key::FalseValue,
EMPTY_STRING => Key::String { str: "" },
@ -618,63 +529,41 @@ impl InternPool {
ty: SimpleType::ISize,
})
}
// Assumes the type is present in the pool.
fn get_simple_type_unchecked(&self, ty: SimpleType) -> Index {
self.get_assume_present(&Key::SimpleType { ty })
}
pub fn get_u0_type(&self) -> Index {
self.get_simple_type_unchecked(SimpleType::UInt(0))
self.get_assume_present(&Key::UIntType { bit_width: 0 })
}
pub fn get_i0_type(&self) -> Index {
self.get_simple_type_unchecked(SimpleType::SInt(0))
self.get_assume_present(&Key::SIntType { bit_width: 0 })
}
pub fn get_u1_type(&self) -> Index {
self.get_simple_type_unchecked(SimpleType::UInt(1))
self.get_assume_present(&Key::UIntType { bit_width: 1 })
}
pub fn get_i1_type(&self) -> Index {
self.get_simple_type_unchecked(SimpleType::SInt(1))
self.get_assume_present(&Key::SIntType { bit_width: 1 })
}
pub fn get_u8_type(&self) -> Index {
self.get_simple_type_unchecked(SimpleType::UInt(8))
self.get_assume_present(&Key::UIntType { bit_width: 8 })
}
pub fn get_i8_type(&self) -> Index {
self.get_simple_type_unchecked(SimpleType::SInt(8))
self.get_assume_present(&Key::SIntType { bit_width: 8 })
}
pub fn get_u16_type(&self) -> Index {
self.get_simple_type_unchecked(SimpleType::UInt(16))
self.get_assume_present(&Key::UIntType { bit_width: 16 })
}
pub fn get_i16_type(&self) -> Index {
self.get_simple_type_unchecked(SimpleType::SInt(16))
self.get_assume_present(&Key::SIntType { bit_width: 16 })
}
pub fn get_u32_type(&self) -> Index {
self.get_simple_type_unchecked(SimpleType::UInt(32))
self.get_assume_present(&Key::UIntType { bit_width: 32 })
}
pub fn get_i32_type(&self) -> Index {
self.get_simple_type_unchecked(SimpleType::SInt(32))
self.get_assume_present(&Key::SIntType { bit_width: 32 })
}
pub fn get_u64_type(&self) -> Index {
self.get_simple_type_unchecked(SimpleType::UInt(64))
self.get_assume_present(&Key::UIntType { bit_width: 64 })
}
pub fn get_i64_type(&self) -> Index {
self.get_simple_type_unchecked(SimpleType::SInt(64))
}
pub fn get_u128_type(&self) -> Index {
self.get_simple_type_unchecked(SimpleType::UInt(128))
}
pub fn get_i128_type(&self) -> Index {
self.get_simple_type_unchecked(SimpleType::SInt(128))
}
pub fn get_top_type(&self) -> Index {
self.get_assume_present(&Key::SimpleType {
ty: SimpleType::Top,
})
}
pub fn get_bottom_type(&self) -> Index {
self.get_assume_present(&Key::SimpleType {
ty: SimpleType::Bottom,
})
self.get_assume_present(&Key::SIntType { bit_width: 64 })
}
}
@ -685,17 +574,6 @@ pub struct TypeInfo {
pub signed: bool,
}
impl TypeInfo {
/// byte size
pub fn size(&self) -> u32 {
self.bitsize.div_ceil(8)
}
/// byte align
pub fn align(&self) -> u32 {
self.bitalign.div_ceil(8)
}
}
impl InternPool {
pub fn peer_type(&mut self, lhs: Index, rhs: Index) -> Option<Index> {
if lhs == rhs {
@ -722,9 +600,8 @@ impl InternPool {
| Key::SimpleType {
ty: SimpleType::ISize,
}
| Key::SimpleType {
ty: SimpleType::SInt(_) | SimpleType::UInt(_),
},
| Key::SIntType { .. }
| Key::UIntType { .. },
) => Some(rhs),
(
Key::SimpleType {
@ -739,9 +616,8 @@ impl InternPool {
| Key::SimpleType {
ty: SimpleType::ISize,
}
| Key::SimpleType {
ty: SimpleType::SInt(_) | SimpleType::UInt(_),
},
| Key::SIntType { .. }
| Key::UIntType { .. },
Key::SimpleType {
ty: SimpleType::ComptimeInt,
},
@ -769,6 +645,15 @@ impl InternPool {
pub fn to_mir_type(&self, index: Index, _ptr_size: TypeInfo) -> crate::mir::Type {
use crate::mir::Type;
match self.get_key(index) {
Key::UIntType { bit_width: bits } => {
let bits = bits as u32;
Type::from_bitsize_int(bits)
}
Key::SIntType { bit_width: bits } => {
let bits = bits as u32;
Type::from_bitsize_int(bits)
}
Key::SimpleType { ty } => match ty {
SimpleType::F32 => Type::SinglePrecision,
SimpleType::F64 => Type::DoublePrecision,
@ -777,11 +662,8 @@ impl InternPool {
todo!("void can't be turned into a mir type")
}
SimpleType::ISize | SimpleType::USize => Type::QWord,
SimpleType::UInt(bits) | SimpleType::SInt(bits) => {
Type::from_bitsize_int(bits as u32)
}
SimpleType::Top | SimpleType::Bottom | SimpleType::ComptimeInt => {
panic!("{ty} can't be turned into a mir type")
SimpleType::ComptimeInt => {
panic!("comptime int can't be turned into a mir type")
}
},
Key::ArrayType { .. } => {
@ -800,9 +682,11 @@ impl InternPool {
pub fn is_type_signed(&self, index: Index, _ptr_size: TypeInfo) -> bool {
match self.get_key(index) {
Key::UIntType { .. } => false,
Key::SIntType { .. } => true,
Key::SimpleType { ty } => match ty {
SimpleType::USize | SimpleType::UInt(_) => false,
SimpleType::ISize | SimpleType::SInt(_) => true,
SimpleType::USize => false,
SimpleType::ISize => true,
_ => false,
},
Key::PointerType { .. } => false,
@ -815,17 +699,23 @@ impl InternPool {
pub fn size_of_type(&self, index: Index, ptr_size: TypeInfo) -> TypeInfo {
match self.get_key(index) {
Key::SimpleType { ty } => match ty {
SimpleType::SInt(bits) => TypeInfo {
bitsize: bits as u32,
bitalign: (bits as u32).next_multiple_of(8).next_power_of_two(),
signed: true,
},
SimpleType::UInt(bits) => TypeInfo {
bitsize: bits as u32,
bitalign: (bits as u32).next_multiple_of(8).next_power_of_two(),
Key::UIntType { bit_width: bits } => {
let bits = bits as u32;
TypeInfo {
bitsize: bits,
bitalign: bits.next_multiple_of(8).next_power_of_two(),
signed: false,
},
}
}
Key::SIntType { bit_width: bits } => {
let bits = bits as u32;
TypeInfo {
bitsize: bits,
bitalign: bits.next_multiple_of(8).next_power_of_two(),
signed: true,
}
}
Key::SimpleType { ty } => match ty {
SimpleType::F32 => TypeInfo {
bitsize: 32,
bitalign: 32,
@ -848,9 +738,6 @@ impl InternPool {
},
SimpleType::USize => ptr_size,
SimpleType::ISize => ptr_size,
SimpleType::Top | SimpleType::Bottom => {
panic!("top and bottom types are not sized")
}
SimpleType::ComptimeInt => panic!("comptime int is unsized"),
},
Key::PointerType { .. } => ptr_size,
@ -899,13 +786,9 @@ impl InternPool {
crate::ast::Type::ComptimeNumber => self.get_comptime_int_type(),
crate::ast::Type::Integer(i) => self.get_or_insert({
if i.signed {
Key::SimpleType {
ty: SimpleType::SInt(i.bits),
}
Key::SIntType { bit_width: i.bits }
} else {
Key::SimpleType {
ty: SimpleType::UInt(i.bits),
}
Key::UIntType { bit_width: i.bits }
}
}),
crate::ast::Type::Floating(crate::ast::FloatingType::Binary32) => self.get_f32_type(),
@ -944,13 +827,9 @@ impl InternPool {
crate::ast::Type::ComptimeNumber => self.get_comptime_int_type(),
crate::ast::Type::Integer(i) => self.get_assume_present(&{
if i.signed {
Key::SimpleType {
ty: SimpleType::SInt(i.bits),
}
Key::SIntType { bit_width: i.bits }
} else {
Key::SimpleType {
ty: SimpleType::UInt(i.bits),
}
Key::UIntType { bit_width: i.bits }
}
}),
crate::ast::Type::Floating(crate::ast::FloatingType::Binary32) => self.get_f32_type(),
@ -986,19 +865,16 @@ impl InternPool {
pub fn as_ast1_type(&self, pointer_bits: u16, index: Index) -> crate::ast::Type {
use crate::ast::Type;
match self.get_key(index) {
Key::UIntType { bit_width: bits } => Type::Integer(IntegralType::new(false, bits)),
Key::SIntType { bit_width: bits } => Type::Integer(IntegralType::new(true, bits)),
Key::SimpleType { ty } => match ty {
SimpleType::F32 => Type::Floating(crate::ast::FloatingType::Binary32),
SimpleType::F64 => Type::Floating(crate::ast::FloatingType::Binary64),
SimpleType::SInt(bits) => Type::Integer(IntegralType::new(true, bits)),
SimpleType::UInt(bits) => Type::Integer(IntegralType::new(false, bits)),
SimpleType::Bool => Type::Bool,
SimpleType::Void => Type::Void,
SimpleType::USize => Type::Integer(IntegralType::new(false, pointer_bits)),
SimpleType::ISize => Type::Integer(IntegralType::new(true, pointer_bits)),
SimpleType::ComptimeInt => Type::comptime_number(),
SimpleType::Top | SimpleType::Bottom => {
panic!("top and bottom types cannot be converted to ast1 types")
}
},
Key::PointerType { pointee, flags } => Type::Pointer {
constness: flags.is_const,
@ -1116,7 +992,9 @@ impl InternPool {
self.create_item(Tag::NegativeInt, i)
}
Key::SimpleType { ty } => self.create_item(Tag::SimpleType, ty.into()),
Key::UIntType { bit_width: bits } => self.create_item(Tag::UIntType, bits as u32),
Key::SIntType { bit_width: bits } => self.create_item(Tag::SIntType, bits as u32),
Key::SimpleType { ty } => self.create_item(Tag::SimpleType, ty as u8 as u32),
Key::PointerType { pointee, flags } => {
let flags = flags.pack();
let i = self.extend_words([pointee.index() as u32, flags as u32]);
@ -1246,11 +1124,17 @@ impl InternPool {
let bigint = BigInt::from_biguint(Sign::Plus, data);
Key::PositiveInt { bigint }
}
Tag::SIntType => Key::SIntType {
bit_width: item.index as u16,
},
Tag::UIntType => Key::UIntType {
bit_width: item.index as u16,
},
Tag::SimpleType => {
let ty = item.idx() as u32;
let ty = item.idx() as u8;
Key::SimpleType {
ty: SimpleType::from(ty),
ty: unsafe { core::mem::transmute::<u8, SimpleType>(ty) },
}
}
Tag::PointerType => {
@ -1347,12 +1231,8 @@ impl InternPool {
pub fn get_int_type(&mut self, signed: bool, bits: u16) -> Index {
let key = match signed {
true => Key::SimpleType {
ty: SimpleType::SInt(bits),
},
false => Key::SimpleType {
ty: SimpleType::UInt(bits),
},
true => Key::SIntType { bit_width: bits },
false => Key::UIntType { bit_width: bits },
};
self.get_or_insert(key)
@ -1417,20 +1297,6 @@ impl InternPool {
})
}
pub fn try_get_pointee_type(&self, pointer: Index) -> Option<Index> {
match self.get_key(pointer) {
Key::PointerType { pointee, .. } | Key::ArrayType { pointee, .. } => Some(pointee),
_ => None,
}
}
pub fn try_get_return_type(&self, func: Index) -> Option<Index> {
match self.get_key(func) {
Key::FunctionType { return_type, .. } => Some(return_type),
_ => None,
}
}
pub fn get_pointer_type(&mut self, pointee: Index, flags: Option<PointerFlags>) -> Index {
let key = Key::PointerType {
pointee,
@ -1438,7 +1304,6 @@ impl InternPool {
};
self.get_or_insert(key)
}
pub fn try_get_pointer_type(
&self,
pointee: Index,
@ -1549,7 +1414,7 @@ impl InternPool {
((index.index() as u32) < self.len()).then_some(index)
}
pub(super) fn get_item(&self, index: Index) -> Option<Item> {
fn get_item(&self, index: Index) -> Option<Item> {
self.check_bounds(index).map(|i| Item {
tag: self.tags[i.index()],
index: self.indices[i.index()],
@ -1568,6 +1433,8 @@ impl InternPool {
| Key::ArrayType { .. }
| Key::PointerType { .. }
| Key::SimpleType { .. }
| Key::SIntType { .. }
| Key::UIntType { .. }
| Key::StructType { .. }
)
}

View file

@ -1,34 +0,0 @@
use super::*;
use core::hash::Hash;
use std::hash::Hasher;
// Types implementing this trait can be stored in the internpool.
trait KeyTrait: Hash + Eq {
const TAG: Tag;
fn serialise(self, pool: &mut InternPool);
fn deserialise(index: Index, pool: &mut InternPool) -> Self;
}
impl KeyTrait for String {
const TAG: Tag = Tag::String;
fn serialise(self, pool: &mut InternPool) {
todo!()
}
fn deserialise(index: Index, pool: &mut InternPool) -> Self {
// let mut hasher = std::hash::DefaultHasher::new();
// core::any::TypeId::of::<Self>().hash(&mut hasher);
// let tag = hasher.finish() as u32;
let item = pool.get_item(index).unwrap();
assert_eq!(item.tag, Self::TAG);
let start = pool.words[item.idx()] as usize;
let len = pool.words[item.idx() + 1] as usize;
let str = unsafe {
let bytes = &pool.strings[start..start + len];
std::str::from_utf8_unchecked(bytes)
};
str.to_owned()
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,631 +0,0 @@
use super::{
intern::{Index as Interned, PointerFlags, StructFlags},
visitor::AstExt,
Ast, Index, ParseError, Tag,
};
pub trait AstNodeExt {
fn get_ast_node(&self, idx: Index) -> AstNode;
}
impl AstNodeExt for &mut Ast {
fn get_ast_node(&self, idx: Index) -> AstNode {
<Ast as AstNodeExt>::get_ast_node(self, idx)
}
}
impl AstNodeExt for &Ast {
fn get_ast_node(&self, idx: Index) -> AstNode {
<Ast as AstNodeExt>::get_ast_node(self, idx)
}
}
impl AstNodeExt for Ast {
fn get_ast_node(&self, idx: Index) -> AstNode {
let (tag, data) = self.get_node_tag_and_data(idx);
match tag {
Tag::Root => {
unreachable!()
}
Tag::File => {
let (a, b) = data.as_extra_range();
let decls = unsafe { Index::from_slice_unchecked(&self.extra[a..b]).to_vec() };
AstNode::File { decls }
}
Tag::FunctionProto => {
let (name, extra) = data.as_intern_and_extra_offset();
let (return_type, parameter_list) = (
Index::from_u32(self.extra[extra]).unwrap(),
Index::from_u32(self.extra[extra + 1]).unwrap(),
);
AstNode::FunctionProto {
name,
return_type,
parameter_list,
}
}
Tag::FunctionProtoInterned => {
let (name, ty) = data.as_two_interns();
AstNode::FunctionProtoInterned { name, ty }
}
Tag::FunctionDecl => {
let (proto, body) = data.as_two_indices();
AstNode::FunctionDecl { proto, body }
}
Tag::ParameterList => {
let (a, b) = data.as_extra_range();
let params = unsafe { Index::from_slice_unchecked(&self.extra[a..b]).to_vec() };
AstNode::ParameterList { params }
}
Tag::Parameter => {
let (ty, name) = data.as_index_intern();
AstNode::Parameter { ty, name }
}
Tag::Block => {
let (a, b) = data.as_extra_range();
let statements = unsafe { Index::from_slice_unchecked(&self.extra[a..b]).to_vec() };
AstNode::Block {
statements,
expr: None,
}
}
Tag::BlockTrailingExpr => {
let (a, b) = data.as_extra_range();
let (expr, statements) = unsafe {
Index::from_slice_unchecked(&self.extra[a..b])
.split_last()
.unwrap()
};
AstNode::Block {
statements: statements.to_vec(),
expr: Some(*expr),
}
}
Tag::Constant => {
let (ty, value) = data.as_index_intern();
AstNode::Constant { ty, value }
}
Tag::ExprStmt => AstNode::ExprStmt {
expr: data.as_index(),
},
Tag::ReturnStmt => AstNode::ReturnStmt,
Tag::ReturnExprStmt => AstNode::ReturnExprStmt {
expr: data.as_index(),
},
Tag::VarDecl => {
let (a, _) = data.as_extra_range();
let name = Interned::from_u32(self.extra[a]);
let ty = Index::from_u32(self.extra[a + 1]).unwrap();
AstNode::VarDecl { name, ty }
}
Tag::MutVarDecl => {
let (a, _) = data.as_extra_range();
let name = Interned::from_u32(self.extra[a]);
let ty = Index::from_u32(self.extra[a + 1]).unwrap();
AstNode::MutVarDecl { name, ty }
}
Tag::VarDeclAssignment => {
let (a, b) = data.as_extra_range();
let extra = &self.extra[a..b];
let name = Interned::from_u32(*extra.get(0).unwrap());
let expr = Index::from_u32(*extra.get(1).unwrap()).unwrap();
let ty = extra.get(2).map(|&inner| Index::from_u32(inner).unwrap());
AstNode::MutVarDeclAssignment { name, expr, ty }
}
Tag::MutVarDeclAssignment => {
let (a, b) = data.as_extra_range();
let extra = &self.extra[a..b];
let name = Interned::from_u32(*extra.get(0).unwrap());
let expr = Index::from_u32(*extra.get(1).unwrap()).unwrap();
let ty = extra.get(2).map(|&inner| Index::from_u32(inner).unwrap());
AstNode::MutVarDeclAssignment { name, expr, ty }
}
Tag::GlobalDecl => {
let (name, offset) = data.as_intern_and_extra_offset();
let ty = Index::from_u32(self.extra[offset]).unwrap();
let expr = Index::from_u32(self.extra[offset + 1]).unwrap();
AstNode::GlobalDecl { name, expr, ty }
}
Tag::StructDecl => {
let (name, offset) = data.as_intern_and_extra_offset();
let flags = StructFlags::unpack(self.extra[offset]);
let types = (offset + 1)..(offset + 1 + flags.num_fields as usize);
let names = (offset + 1 + flags.num_fields as usize)
..(offset + 1 + flags.num_fields as usize * 2);
let field_types =
unsafe { Index::from_slice_unchecked(&self.extra[types]).to_vec() };
let field_names = self.extra[names]
.iter()
.map(|&i| Interned::from_u32(i))
.collect();
AstNode::StructDecl {
name,
flags,
field_names,
field_types,
}
}
Tag::StructDeclInterned => {
let (name, ty) = data.as_two_interns();
AstNode::StructDeclInterned { name, ty }
}
Tag::FieldDecl => {
let (ty, name) = data.as_index_intern();
AstNode::FieldDecl { name, ty }
}
Tag::DeclRef => AstNode::DeclRef {
decl: data.as_index(),
},
Tag::DeclRefUnresolved => {
let (scope, name) = data.as_index_intern();
AstNode::DeclRefUnresolved { scope, name }
}
Tag::InternedType => AstNode::InternedType {
intern: data.as_intern(),
},
Tag::TypeDeclRef => AstNode::TypeDeclRef {
decl: data.as_index(),
},
Tag::TypeDeclRefUnresolved => {
let (scope, name) = data.as_index_intern();
AstNode::TypeDeclRefUnresolved { scope, name }
}
Tag::PointerType => {
let (ty, flags) = data.as_index_and_opaque();
let flags = PointerFlags::unpack(flags as u8);
AstNode::PointerType { ty, flags }
}
Tag::ArrayType => {
let (length, pointer) = data.as_two_indices();
AstNode::ArrayType { length, pointer }
}
Tag::CallExpr => {
let (func, argument_list) = data.as_two_indices();
AstNode::CallExpr {
func,
argument_list,
}
}
Tag::FieldAccess => {
let (expr, field_name) = data.as_index_intern();
AstNode::FieldAccess { field_name, expr }
}
Tag::ArgumentList => {
let (a, b) = data.as_extra_range();
let arguments = unsafe { Index::from_slice_unchecked(&self.extra[a..b]).to_vec() };
AstNode::ArgumentList { arguments }
}
Tag::Argument => AstNode::Argument {
expr: data.as_index(),
name: None,
},
Tag::NamedArgument => {
let (expr, name) = data.as_index_intern();
AstNode::Argument {
expr,
name: Some(name),
}
}
Tag::ExplicitCast => {
let (expr, ty) = data.as_two_indices();
AstNode::ExplicitCast { expr, ty }
}
Tag::Deref => AstNode::Deref {
expr: data.as_index(),
},
Tag::AddressOf => AstNode::AddressOf {
expr: data.as_index(),
},
Tag::Not => AstNode::Not {
expr: data.as_index(),
},
Tag::Negate => AstNode::Negate {
expr: data.as_index(),
},
Tag::PlaceToValueConversion => AstNode::PlaceToValueConversion {
expr: data.as_index(),
},
Tag::ValueToPlaceConversion => AstNode::ValueToPlaceConversion {
expr: data.as_index(),
},
Tag::Or => {
let (lhs, rhs) = data.as_two_indices();
AstNode::Or { lhs, rhs }
}
Tag::And => {
let (lhs, rhs) = data.as_two_indices();
AstNode::And { lhs, rhs }
}
Tag::BitOr => {
let (lhs, rhs) = data.as_two_indices();
AstNode::BitOr { lhs, rhs }
}
Tag::BitXOr => {
let (lhs, rhs) = data.as_two_indices();
AstNode::BitXOr { lhs, rhs }
}
Tag::BitAnd => {
let (lhs, rhs) = data.as_two_indices();
AstNode::BitAnd { lhs, rhs }
}
Tag::Eq => {
let (lhs, rhs) = data.as_two_indices();
AstNode::Eq { lhs, rhs }
}
Tag::NEq => {
let (lhs, rhs) = data.as_two_indices();
AstNode::NEq { lhs, rhs }
}
Tag::Lt => {
let (lhs, rhs) = data.as_two_indices();
AstNode::Lt { lhs, rhs }
}
Tag::Gt => {
let (lhs, rhs) = data.as_two_indices();
AstNode::Gt { lhs, rhs }
}
Tag::Le => {
let (lhs, rhs) = data.as_two_indices();
AstNode::Le { lhs, rhs }
}
Tag::Ge => {
let (lhs, rhs) = data.as_two_indices();
AstNode::Ge { lhs, rhs }
}
Tag::Shl => {
let (lhs, rhs) = data.as_two_indices();
AstNode::Shl { lhs, rhs }
}
Tag::Shr => {
let (lhs, rhs) = data.as_two_indices();
AstNode::Shr { lhs, rhs }
}
Tag::Add => {
let (lhs, rhs) = data.as_two_indices();
AstNode::Add { lhs, rhs }
}
Tag::Sub => {
let (lhs, rhs) = data.as_two_indices();
AstNode::Sub { lhs, rhs }
}
Tag::Mul => {
let (lhs, rhs) = data.as_two_indices();
AstNode::Mul { lhs, rhs }
}
Tag::Div => {
let (lhs, rhs) = data.as_two_indices();
AstNode::Div { lhs, rhs }
}
Tag::Rem => {
let (lhs, rhs) = data.as_two_indices();
AstNode::Rem { lhs, rhs }
}
Tag::Assign => {
let (lhs, rhs) = data.as_two_indices();
AstNode::Assign { lhs, rhs }
}
Tag::SubscriptExpr => {
let (lhs, index) = data.as_two_indices();
AstNode::SubscriptExpr { lhs, index }
}
Tag::IfExpr => {
let (cond, body) = data.as_two_indices();
AstNode::IfExpr { cond, body }
}
Tag::IfElseExpr => {
let (cond, extra) = data.as_index_and_extra_offset();
let [a, b] = self.extra[extra..][..2] else {
unreachable!()
};
AstNode::IfElseExpr {
cond,
a: Index::from_u32(a).unwrap(),
b: Index::from_u32(b).unwrap(),
}
}
Tag::Error => AstNode::Error {
err: data.as_error(),
},
Tag::Undefined => AstNode::Undefined,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AstNode {
/// pseudo tag, contains a range from a..b into extra of all files.
Root {
files: Vec<Index>,
},
/// `data` is a range from a..b into extra of all global nodes.
File {
decls: Vec<Index>,
},
/// `data` is an intern to a name, and an index into extra of [index: return_type, index: ParameterList]
FunctionProto {
name: Interned,
return_type: Index,
parameter_list: Index,
},
/// `data` is an intern to a name, and an intern to the function type
FunctionProtoInterned {
name: Interned,
ty: Interned,
},
/// `data` is an index to a FunctionProto and an index to a Block
FunctionDecl {
proto: Index,
body: Index,
},
/// `data` is a range from a..b into extra of indices to parameters
ParameterList {
params: Vec<Index>,
},
/// `data` is an index to a type, and an intern to a name
Parameter {
ty: Index,
name: Interned,
},
/// `data` is range from a..b into `extra` of indices to statements
Block {
statements: Vec<Index>,
expr: Option<Index>,
},
/// `data` is an index to a type, and an intern to a value
Constant {
ty: Index,
value: Interned,
},
/// `data` is an index to an expression
ExprStmt {
expr: Index,
},
/// `data` is none
ReturnStmt,
/// `data` is an index to an expr
ReturnExprStmt {
expr: Index,
},
/// `data` is a range from a..b into `extra` of `[name: intern, type: index]`
VarDecl {
name: Interned,
ty: Index,
},
/// `data` is a range from a..b into `extra` of `[name: intern, type: index]`
MutVarDecl {
name: Interned,
ty: Index,
},
/// `data` is a range from a..b into `extra` of `[name: intern, expr: index, type?: index]`
VarDeclAssignment {
name: Interned,
expr: Index,
ty: Option<Index>,
},
/// `data` is a range from a..b into `extra` of `[name: intern, expr: index, type?: index]`
MutVarDeclAssignment {
name: Interned,
expr: Index,
ty: Option<Index>,
},
/// `data` is an intern to a name, and an offset into `extra` of `[type: index, expr: index]`
GlobalDecl {
name: Interned,
expr: Index,
ty: Index,
},
/// `data` is an intern to a name, and an offset into extra of `[flags, type0 ,..., typeN ,name0 ,..., nameN]`
StructDecl {
name: Interned,
flags: StructFlags,
field_names: Vec<Interned>,
field_types: Vec<Index>,
},
/// `data` is an intern to a name, and an intern to the type of the struct
StructDeclInterned {
name: Interned,
ty: Interned,
},
/// `data` is an index to a type, and an intern to a name
FieldDecl {
name: Interned,
ty: Index,
},
/// `data` is an index to a Parameter, VarDecl, GlobalDecl or FunctionDecl, and an opaque DeclKind
DeclRef {
decl: Index,
},
/// `data` is an inlined key into the symbol table (scope: index, name: intern)
DeclRefUnresolved {
scope: Index,
name: Interned,
},
/// `data` is an intern of a type
InternedType {
intern: Interned,
},
/// `data` is an index to a StructDecl
TypeDeclRef {
decl: Index,
},
/// `data` is an inlined key into the symbol table (scope: index, name: intern)
TypeDeclRefUnresolved {
scope: Index,
name: Interned,
},
/// `data` is an index to a Type and u32 PointerFlags (extra offset)
PointerType {
ty: Index,
flags: PointerFlags,
},
/// `data` is an index to a length expression, and an underlying pointer type
ArrayType {
length: Index,
pointer: Index,
},
/// `data` is an index to an expr and an index to an ArgumentList
CallExpr {
func: Index,
argument_list: Index,
},
/// `data` is an index to an expr and an intern to a field name
FieldAccess {
field_name: Interned,
expr: Index,
},
/// `data` is a range from a..b into extra of indices to arguments
ArgumentList {
arguments: Vec<Index>,
},
/// `data` is an index to an expression
Argument {
expr: Index,
name: Option<Interned>,
},
/// `data` is an index to lhs, and an index to the type
ExplicitCast {
expr: Index,
ty: Index,
},
/// `data` is a single index to an expr
Deref {
expr: Index,
},
AddressOf {
expr: Index,
},
Not {
expr: Index,
},
Negate {
expr: Index,
},
PlaceToValueConversion {
expr: Index,
},
ValueToPlaceConversion {
expr: Index,
},
/// data is two indices for `lhs` and `rhs`
Or {
lhs: Index,
rhs: Index,
},
And {
lhs: Index,
rhs: Index,
},
BitOr {
lhs: Index,
rhs: Index,
},
BitXOr {
lhs: Index,
rhs: Index,
},
BitAnd {
lhs: Index,
rhs: Index,
},
Eq {
lhs: Index,
rhs: Index,
},
NEq {
lhs: Index,
rhs: Index,
},
Lt {
lhs: Index,
rhs: Index,
},
Gt {
lhs: Index,
rhs: Index,
},
Le {
lhs: Index,
rhs: Index,
},
Ge {
lhs: Index,
rhs: Index,
},
Shl {
lhs: Index,
rhs: Index,
},
Shr {
lhs: Index,
rhs: Index,
},
Add {
lhs: Index,
rhs: Index,
},
Sub {
lhs: Index,
rhs: Index,
},
Mul {
lhs: Index,
rhs: Index,
},
Div {
lhs: Index,
rhs: Index,
},
Rem {
lhs: Index,
rhs: Index,
},
Assign {
lhs: Index,
rhs: Index,
},
SubscriptExpr {
lhs: Index,
index: Index,
},
IfExpr {
cond: Index,
body: Index,
},
/// `data` is an index to an expression and an index into extra for [if, else]
IfElseExpr {
cond: Index,
a: Index,
b: Index,
},
// TODO:
/// `data` is a ParseError
Error {
err: ParseError,
},
/// placeholder tag for reserved indices/nodes, `data` is none
Undefined,
}

View file

@ -1,502 +0,0 @@
// Visitor pattern has lots of unused arguments
#![allow(unused_variables)]
use crate::variant;
use super::{
Ast, Data, Index, Tag,
intern::{self},
tag::{AstNode, AstNodeExt},
visitor::{AstExt, AstVisitorTrait},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ErrorKind {
MismatchingTypes,
DerefNonPointer,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Error {
idx: Index,
kind: ErrorKind,
}
struct TypeChecker<'a> {
ip: &'a mut intern::InternPool,
errors: Vec<Error>,
}
impl<'a> TypeChecker<'a> {
fn visit_children<'b>(
&mut self,
ast: &'b mut super::Ast,
idx: Index,
) -> Result<Option<intern::Index>, ()> {
for child in ast.get_node_children(idx) {
self.visit_any(ast, child)?;
}
Ok(None)
}
}
// TODO: actually handle errors here instead of aborting.
impl<'a> AstVisitorTrait<&'a mut Ast> for TypeChecker<'_> {
type Error = ();
type Value = Option<intern::Index>;
const UNIMPL: Self::Error = ();
fn visit_parameter_impl(
&mut self,
ast: &'a mut Ast,
idx: Index,
ty: Index,
name: intern::Index,
) -> Result<Self::Value, Self::Error> {
_ = (idx, name);
self.visit_any(ast, ty)
}
fn visit_array_type_impl(
&mut self,
ast: &'a mut Ast,
idx: Index,
length: Index,
pointer: Index,
) -> Result<Self::Value, Self::Error> {
_ = self.visit_any(ast, length)?;
let pointee = self.visit_any(ast, pointer)?.expect("no type?");
variant!(self.ip.get_key(pointee) => intern::Key::PointerType { pointee, flags });
// get interened value from constant node
let length = {
let value = ast.datas[length].as_index_intern().1;
match self.ip.get_key(value) {
intern::Key::SIntSmall { bits } => bits as u32,
intern::Key::UIntSmall { bits } => bits as u32,
intern::Key::SInt64 { bits } => bits as u32,
intern::Key::UInt64 { bits } => bits as u32,
intern::Key::NegativeInt { bigint } | intern::Key::PositiveInt { bigint } => {
bigint.iter_u32_digits().next().unwrap_or(0)
}
_ => 0,
}
};
let ty = self.ip.get_array_type(pointee, Some(flags), length);
ast.tags[idx] = Tag::InternedType;
ast.datas[idx] = Data::intern(ty);
Ok(Some(ty))
}
fn visit_pointer_type_impl(
&mut self,
ast: &'a mut Ast,
idx: Index,
ty: Index,
flags: intern::PointerFlags,
) -> Result<Self::Value, Self::Error> {
let pointee = self.visit_any(ast, ty)?.expect("no type?");
let ty = self.ip.get_pointer_type(pointee, Some(flags));
ast.tags[idx] = Tag::InternedType;
ast.datas[idx] = Data::intern(ty);
Ok(Some(ty))
}
fn visit_type_decl_ref_impl(
&mut self,
ast: &'a mut Ast,
idx: Index,
decl: Index,
) -> Result<Self::Value, Self::Error> {
let ty = self.visit_any(ast, decl)?.expect("no type?");
ast.tags[idx] = Tag::InternedType;
ast.datas[idx] = Data::intern(ty);
Ok(Some(ty))
}
fn visit_function_proto_impl(
&mut self,
ast: &'a mut Ast,
idx: Index,
name: intern::Index,
return_type: Index,
parameter_list: Index,
) -> Result<Self::Value, Self::Error> {
variant!(ast.get_ast_node(parameter_list) => AstNode::ParameterList { params });
let return_type = self.visit_any(ast, return_type)?.expect("no type?");
let params = params
.into_iter()
.map(|param| self.visit_parameter(ast, param).map(|a| a.unwrap()))
.collect::<Result<Vec<_>, _>>()?;
let ty = self.ip.get_function_type(return_type, params);
ast.tags[idx] = Tag::FunctionProtoInterned;
ast.datas[idx] = Data::two_interns(name, ty);
Ok(Some(ty))
}
fn visit_function_proto_interned_impl(
&mut self,
ast: &'a mut Ast,
idx: Index,
name: intern::Index,
ty: intern::Index,
) -> Result<Self::Value, Self::Error> {
Ok(Some(ty))
}
fn visit_struct_decl_impl(
&mut self,
ast: &'a mut Ast,
idx: Index,
name: intern::Index,
flags: intern::StructFlags,
field_names: Vec<intern::Index>,
field_types: Vec<Index>,
) -> Result<Self::Value, Self::Error> {
let types = field_types
.into_iter()
.map(|i| self.visit_any(ast, i).map(Option::unwrap))
.collect::<Result<Vec<_>, _>>()?;
let ty = self.ip.insert_or_replace_struct_type(
name,
idx,
flags.packed,
flags.c_like,
field_names.into_iter().zip(types),
);
ast.tags[idx] = Tag::StructDeclInterned;
ast.datas[idx] = Data::two_interns(name, ty);
Ok(Some(ty))
}
fn visit_function_decl_impl(
&mut self,
ast: &'a mut Ast,
idx: Index,
proto: Index,
body: Index,
) -> Result<Self::Value, Self::Error> {
let ty = self.visit_any(ast, proto)?.expect("no type?");
let body = self.visit_block(ast, body)?.unwrap_or(intern::Index::VOID);
let ret_ty = self.ip.try_get_return_type(ty).expect("no type?");
if body != ret_ty {
self.errors.push(Error {
idx,
kind: ErrorKind::MismatchingTypes,
});
Ok(Some(intern::Index::VOID))
} else {
Ok(Some(ty))
}
}
fn visit_block_impl(
&mut self,
ast: &'a mut Ast,
idx: Index,
statements: Vec<Index>,
expr: Option<Index>,
) -> Result<Self::Value, Self::Error> {
for stmt in statements {
self.visit_any(ast, stmt)?;
}
expr.map(|expr| self.visit_any(ast, expr))
.transpose()
.map(Option::flatten)
}
fn visit_file_impl(
&mut self,
ast: &'a mut Ast,
idx: Index,
decls: Vec<Index>,
) -> Result<Self::Value, Self::Error> {
for decl in decls {
self.visit_any(ast, decl)?;
}
Ok(None)
}
fn visit_struct_decl_interned_impl(
&mut self,
ast: &'a mut Ast,
idx: Index,
name: intern::Index,
ty: intern::Index,
) -> Result<Self::Value, Self::Error> {
Ok(Some(ty))
}
fn visit_interned_type_impl(
&mut self,
ast: &'a mut Ast,
idx: Index,
intern: intern::Index,
) -> Result<Self::Value, Self::Error> {
Ok(Some(intern))
}
fn visit_any(
&mut self,
ast: &'a mut Ast,
idx: super::Index,
) -> Result<Self::Value, Self::Error> {
let (tag, data) = ast.get_node_tag_and_data(idx);
let _ty = match tag {
Tag::Root => unreachable!(),
Tag::InternedType => Some(data.as_intern()),
Tag::File => self.visit_file(ast, idx)?,
Tag::ArrayType => self.visit_array_type(ast, idx)?,
Tag::PointerType => self.visit_pointer_type(ast, idx)?,
Tag::TypeDeclRef => self.visit_type_decl_ref(ast, idx)?,
Tag::FunctionProtoInterned => self.visit_function_proto_interned(ast, idx)?,
Tag::FunctionProto => self.visit_function_proto(ast, idx)?,
Tag::Parameter => self.visit_parameter(ast, idx)?,
Tag::StructDecl => self.visit_struct_decl(ast, idx)?,
Tag::StructDeclInterned => self.visit_struct_decl_interned(ast, idx)?,
Tag::FunctionDecl => self.visit_function_decl(ast, idx)?,
Tag::ParameterList => todo!(),
Tag::Block | Tag::BlockTrailingExpr => self.visit_block(ast, idx)?,
Tag::Constant => self.visit_constant(ast, idx)?,
Tag::ExprStmt => self.visit_expr_stmt(ast, idx)?,
Tag::ReturnStmt => self.visit_return_stmt(ast, idx)?,
Tag::ReturnExprStmt => todo!(),
Tag::VarDecl => todo!(),
Tag::MutVarDecl => todo!(),
Tag::VarDeclAssignment => todo!(),
Tag::MutVarDeclAssignment => todo!(),
Tag::GlobalDecl => todo!(),
Tag::FieldDecl => todo!(),
Tag::DeclRef => todo!(),
Tag::DeclRefUnresolved => todo!(),
Tag::TypeDeclRefUnresolved => todo!(),
Tag::CallExpr => todo!(),
Tag::FieldAccess => todo!(),
Tag::ArgumentList => todo!(),
Tag::Argument => todo!(),
Tag::NamedArgument => todo!(),
Tag::ExplicitCast => self.visit_explicit_cast(ast, idx)?,
Tag::Deref => self.visit_deref(ast, idx)?,
Tag::AddressOf => self.visit_address_of(ast, idx)?,
Tag::Not | Tag::Negate => {
let ty = self.visit_any(ast, data.as_index())?;
ty
}
Tag::PlaceToValueConversion => self.visit_place_to_value_conversion(ast, idx)?,
Tag::ValueToPlaceConversion => self.visit_value_to_place_conversion(ast, idx)?,
Tag::Or
| Tag::And
| Tag::BitOr
| Tag::BitXOr
| Tag::BitAnd
| Tag::Eq
| Tag::NEq
| Tag::Lt
| Tag::Gt
| Tag::Le
| Tag::Ge
| Tag::Shl
| Tag::Shr
| Tag::Add
| Tag::Sub
| Tag::Mul
| Tag::Div
| Tag::Rem => {
let (lhs, rhs) = data.as_two_indices();
let lhs = self.visit_any(ast, lhs)?.unwrap();
let rhs = self.visit_any(ast, rhs)?.unwrap();
if lhs != rhs {
self.errors.push(Error {
idx,
kind: ErrorKind::MismatchingTypes,
});
}
Some(lhs)
}
Tag::Assign => self.visit_assign(ast, idx)?,
Tag::SubscriptExpr => self.visit_subscript_expr(ast, idx)?,
Tag::IfExpr => self.visit_if_expr(ast, idx)?,
Tag::IfElseExpr => self.visit_if_else_expr(ast, idx)?,
Tag::Error => todo!(),
Tag::Undefined => todo!(),
};
todo!()
}
fn visit_explicit_cast_impl(
&mut self,
ast: &'a mut Ast,
idx: Index,
expr: Index,
ty: Index,
) -> Result<Self::Value, Self::Error> {
_ = self.visit_any(ast, expr)?;
// TODO: make sure this cast is legal
let ty = self.visit_any(ast, ty)?;
Ok(ty)
}
fn visit_address_of_impl(
&mut self,
ast: &'a mut Ast,
idx: Index,
expr: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_any(ast, expr)
}
fn visit_deref_impl(
&mut self,
ast: &'a mut Ast,
idx: Index,
expr: Index,
) -> Result<Self::Value, Self::Error> {
let ty = self.visit_any(ast, expr)?.unwrap();
if let intern::Key::PointerType { pointee, .. } = self.ip.get_key(ty) {
Ok(Some(pointee))
} else {
Ok(None)
}
}
fn visit_place_to_value_conversion_impl(
&mut self,
ast: &'a mut Ast,
idx: Index,
expr: Index,
) -> Result<Self::Value, Self::Error> {
let ty = self.visit_any(ast, expr)?;
if let Some(ty) = ty {
Ok(self.ip.try_get_pointee_type(ty))
} else {
self.errors.push(Error {
idx,
kind: ErrorKind::DerefNonPointer,
});
Ok(None)
}
}
fn visit_value_to_place_conversion_impl(
&mut self,
ast: &'a mut Ast,
idx: Index,
expr: Index,
) -> Result<Self::Value, Self::Error> {
let ty = self.visit_any(ast, expr)?;
if let Some(ty) = ty {
Ok(Some(self.ip.get_pointer_type(ty, None)))
} else {
Ok(None)
}
}
fn visit_if_else_expr_impl(
&mut self,
ast: &'a mut Ast,
idx: Index,
cond: Index,
a: Index,
b: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_any(ast, cond)?;
let a = self.visit_block(ast, a)?;
let b = self.visit_block(ast, b)?;
if a != b {
self.errors.push(Error {
idx,
kind: ErrorKind::MismatchingTypes,
});
}
Ok(a)
}
fn visit_if_expr_impl(
&mut self,
ast: &'a mut Ast,
idx: Index,
cond: Index,
body: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_any(ast, cond)?;
self.visit_block(ast, body)?;
Ok(None)
}
fn visit_subscript_expr_impl(
&mut self,
ast: &'a mut Ast,
idx: Index,
lhs: Index,
index: Index,
) -> Result<Self::Value, Self::Error> {
let lhs = self.visit_any(ast, lhs)?.unwrap();
let index = self.visit_any(ast, index)?.unwrap();
if index != intern::Index::USIZE {
self.errors.push(Error {
idx,
kind: ErrorKind::MismatchingTypes,
});
}
let pointee = self.ip.try_get_pointee_type(lhs).unwrap();
let pointer = self.ip.get_pointer_type(pointee, None);
Ok(Some(pointer))
}
fn visit_assign_impl(
&mut self,
ast: &'a mut Ast,
idx: Index,
lhs: Index,
rhs: Index,
) -> Result<Self::Value, Self::Error> {
let lhs = self.visit_any(ast, lhs)?.unwrap();
let rhs = self.visit_any(ast, rhs)?.unwrap();
if self.ip.try_get_pointee_type(lhs) != Some(rhs) {
self.errors.push(Error {
idx,
kind: ErrorKind::MismatchingTypes,
});
}
Ok(None)
}
}

View file

@ -1,625 +0,0 @@
use crate::{ast2::tag::AstNode, variant};
use super::{tag::AstNodeExt, *};
pub trait AstExt {
fn get_node_children(&self, node: Index) -> Vec<Index>;
fn get_node_tag_and_data(&self, node: Index) -> (Tag, Data);
fn get_node_data_for_tag(&self, idx: Index, tag: Tag) -> Option<Data> {
let (t, data) = self.get_node_tag_and_data(idx);
if t == tag {
Some(data)
} else {
None
}
}
fn expect_node_data_for_tag(&self, idx: Index, tag: Tag) -> Data {
self.get_node_data_for_tag(idx, tag)
.expect(&format!("node {idx} is not a {tag:?}"))
}
}
impl AstExt for &Ast {
fn get_node_children(&self, node: Index) -> Vec<Index> {
Ast::get_node_children(self, node)
}
fn get_node_tag_and_data(&self, node: Index) -> (Tag, Data) {
(self.tags[node], self.datas[node])
}
}
impl AstExt for &mut Ast {
fn get_node_children(&self, node: Index) -> Vec<Index> {
Ast::get_node_children(self, node)
}
fn get_node_tag_and_data(&self, node: Index) -> (Tag, Data) {
(self.tags[node], self.datas[node])
}
}
pub struct AstVisitor<AstT> {
pub(super) ast: AstT,
pub(super) scopes: Vec<Index>,
pub(super) nodes: Vec<A>,
pub(super) rev: bool,
}
impl<AstT> AstVisitor<AstT>
where
AstT: AstExt,
{
pub fn visit<
F: FnMut(&mut AstT, &[Index], Index, Tag, Data),
G: FnMut(&mut AstT, &[Index], Index, Tag, Data),
>(
&mut self,
mut pre: F,
mut post: G,
) {
while let Some(node) = self.nodes.pop() {
match node {
A::PushChildren(i) => {
self.nodes.push(A::PopSelf(i));
let children_iter = self
.ast
.get_node_children(i)
.into_iter()
.map(|i| A::PushChildren(i));
// inverse because we are popping from the end
if !self.rev {
self.nodes.extend(children_iter.rev())
} else {
self.nodes.extend(children_iter)
};
let (tag, data) = self.ast.get_node_tag_and_data(i);
let _ = pre(&mut self.ast, &self.scopes, i, tag, data);
match tag {
Tag::File
| Tag::FunctionDecl
| Tag::GlobalDecl
| Tag::Block
| Tag::BlockTrailingExpr => {
self.scopes.push(i);
}
_ => {}
}
}
A::PopSelf(i) => {
// already popped.
let (tag, data) = self.ast.get_node_tag_and_data(i);
let _ = post(&mut self.ast, &self.scopes, i, tag, data);
match tag {
Tag::File
| Tag::FunctionDecl
| Tag::GlobalDecl
| Tag::Block
| Tag::BlockTrailingExpr => {
self.scopes.pop();
}
_ => {}
}
}
}
}
}
pub fn visit_pre<F: FnMut(&mut AstT, &[Index], Index, Tag, Data)>(&mut self, mut cb: F) {
while let Some(node) = self.nodes.pop() {
match node {
A::PushChildren(i) => {
self.nodes.push(A::PopSelf(i));
let children_iter = self
.ast
.get_node_children(i)
.into_iter()
.map(|i| A::PushChildren(i));
// inverse because we are popping from the end
if !self.rev {
self.nodes.extend(children_iter.rev())
} else {
self.nodes.extend(children_iter)
};
let (tag, data) = self.ast.get_node_tag_and_data(i);
let _ = cb(&mut self.ast, &self.scopes, i, tag, data);
match tag {
Tag::File
| Tag::FunctionDecl
| Tag::GlobalDecl
| Tag::Block
| Tag::BlockTrailingExpr => {
self.scopes.push(i);
}
_ => {}
}
}
A::PopSelf(i) => {
// already popped.
let (tag, _data) = self.ast.get_node_tag_and_data(i);
match tag {
Tag::File
| Tag::FunctionDecl
| Tag::GlobalDecl
| Tag::Block
| Tag::BlockTrailingExpr => {
self.scopes.pop();
}
_ => {}
}
}
}
}
}
pub fn visit_post<F: FnMut(&mut AstT, &[Index], Index, Tag, Data)>(&mut self, mut cb: F) {
while let Some(node) = self.nodes.pop() {
match node {
A::PushChildren(i) => {
self.nodes.push(A::PopSelf(i));
let children_iter = self
.ast
.get_node_children(i)
.into_iter()
.map(|i| A::PushChildren(i));
if self.rev {
self.nodes.extend(children_iter.rev())
} else {
self.nodes.extend(children_iter)
};
let (tag, _data) = self.ast.get_node_tag_and_data(i);
match tag {
Tag::File
| Tag::FunctionDecl
| Tag::GlobalDecl
| Tag::Block
| Tag::BlockTrailingExpr => {
self.scopes.push(i);
}
_ => {}
}
}
A::PopSelf(i) => {
// already popped.
let (tag, data) = self.ast.get_node_tag_and_data(i);
let _ = cb(&mut self.ast, &self.scopes, i, tag, data);
match tag {
Tag::File
| Tag::FunctionDecl
| Tag::GlobalDecl
| Tag::Block
| Tag::BlockTrailingExpr => {
self.scopes.pop();
}
_ => {}
}
}
}
}
}
}
pub enum A {
PushChildren(Index),
PopSelf(Index),
}
impl Ast {
pub fn visitor_mut(&mut self) -> AstVisitor<&mut Self> {
AstVisitor {
scopes: vec![],
nodes: self
.get_root_file_indices()
.map(|i| visitor::A::PushChildren(i))
.collect(),
ast: self,
rev: false,
}
}
pub fn visitor_rev_mut(&mut self) -> AstVisitor<&mut Self> {
AstVisitor {
scopes: vec![],
nodes: self
.get_root_file_indices()
.map(|i| visitor::A::PushChildren(i))
.collect(),
ast: self,
rev: true,
}
}
pub fn visitor(&self) -> AstVisitor<&Self> {
AstVisitor {
scopes: vec![],
nodes: self
.get_root_file_indices()
.map(|i| visitor::A::PushChildren(i))
.collect(),
ast: self,
rev: false,
}
}
}
macro_rules! tag_visit_fn {
($($tag:tt {$($field_name:ident : $field_ty:ty),* $(,)?}),* $(,)?) => {
$(
paste::paste! {
fn [<visit_ $tag:snake>](&mut self, ast: Ast, idx: Index) -> Result<Self::Value, Self::Error> {
variant!(ast.get_ast_node(idx) => AstNode::$tag { $($field_name),* });
self.[<visit_ $tag:snake _impl>](ast, idx, $($field_name),*)
}
fn [<visit_ $tag:snake _impl>](&mut self, ast: Ast, idx: Index, $($field_name: $field_ty),*) -> Result<Self::Value, Self::Error> {
_ = (ast, idx, $($field_name),*);
Err(Self::UNIMPL)
}
}
)*
};
}
use intern::Index as Interned;
pub trait AstVisitorTrait<Ast: AstExt + AstNodeExt> {
type Error;
type Value;
const UNIMPL: Self::Error;
tag_visit_fn!(
File {
decls: Vec<Index>,
},
FunctionProto {
name: Interned,
return_type: Index,
parameter_list: Index,
},
FunctionProtoInterned {
name: Interned,
ty: Interned,
},
FunctionDecl {
proto: Index,
body: Index,
},
ParameterList {
params: Vec<Index>,
},
Parameter {
ty: Index,
name: Interned,
},
Block {
statements: Vec<Index>,
expr: Option<Index>,
},
Constant {
ty: Index,
value: Interned,
},
ExprStmt {
expr: Index,
},
ReturnStmt {},
ReturnExprStmt {
expr: Index,
},
VarDecl {
name: Interned,
ty: Index,
},
MutVarDecl {
name: Interned,
ty: Index,
},
VarDeclAssignment {
name: Interned,
expr: Index,
ty: Option<Index>,
},
MutVarDeclAssignment {
name: Interned,
expr: Index,
ty: Option<Index>,
},
GlobalDecl {
name: Interned,
expr: Index,
ty: Index,
},
StructDecl {
name: Interned,
flags: StructFlags,
field_names: Vec<Interned>,
field_types: Vec<Index>,
},
StructDeclInterned {
name: Interned,
ty: Interned,
},
FieldDecl {
name: Interned,
ty: Index,
},
DeclRef {
decl: Index,
},
DeclRefUnresolved {
scope: Index,
name: Interned,
},
InternedType {
intern: Interned,
},
TypeDeclRef {
decl: Index,
},
TypeDeclRefUnresolved {
scope: Index,
name: Interned,
},
PointerType {
ty: Index,
flags: PointerFlags,
},
ArrayType {
length: Index,
pointer: Index,
},
CallExpr {
func: Index,
argument_list: Index,
},
FieldAccess {
field_name: Interned,
expr: Index,
},
ArgumentList {
arguments: Vec<Index>,
},
Argument {
expr: Index,
name: Option<Interned>,
},
ExplicitCast {
expr: Index,
ty: Index,
},
Deref {
expr: Index,
},
AddressOf {
expr: Index,
},
Not {
expr: Index,
},
Negate {
expr: Index,
},
PlaceToValueConversion {
expr: Index,
},
ValueToPlaceConversion {
expr: Index,
},
Or {
lhs: Index,
rhs: Index,
},
And {
lhs: Index,
rhs: Index,
},
BitOr {
lhs: Index,
rhs: Index,
},
BitXOr {
lhs: Index,
rhs: Index,
},
BitAnd {
lhs: Index,
rhs: Index,
},
Eq {
lhs: Index,
rhs: Index,
},
NEq {
lhs: Index,
rhs: Index,
},
Lt {
lhs: Index,
rhs: Index,
},
Gt {
lhs: Index,
rhs: Index,
},
Le {
lhs: Index,
rhs: Index,
},
Ge {
lhs: Index,
rhs: Index,
},
Shl {
lhs: Index,
rhs: Index,
},
Shr {
lhs: Index,
rhs: Index,
},
Add {
lhs: Index,
rhs: Index,
},
Sub {
lhs: Index,
rhs: Index,
},
Mul {
lhs: Index,
rhs: Index,
},
Div {
lhs: Index,
rhs: Index,
},
Rem {
lhs: Index,
rhs: Index,
},
Assign {
lhs: Index,
rhs: Index,
},
SubscriptExpr {
lhs: Index,
index: Index,
},
IfExpr {
cond: Index,
body: Index,
},
IfElseExpr {
cond: Index,
a: Index,
b: Index,
},
Error {
err: ParseError,
},
Undefined {},
);
fn visit_any(&mut self, ast: Ast, idx: Index) -> Result<Self::Value, Self::Error> {
match ast.get_ast_node(idx) {
AstNode::File { decls } => self.visit_file_impl(ast, idx, decls),
AstNode::FunctionProto {
name,
return_type,
parameter_list,
} => self.visit_function_proto_impl(ast, idx, name, return_type, parameter_list),
AstNode::FunctionProtoInterned { name, ty } => {
self.visit_function_proto_interned_impl(ast, idx, name, ty)
}
AstNode::FunctionDecl { proto, body } => {
self.visit_function_decl_impl(ast, idx, proto, body)
}
AstNode::ParameterList { params } => self.visit_parameter_list_impl(ast, idx, params),
AstNode::Parameter { ty, name } => self.visit_parameter_impl(ast, idx, ty, name),
AstNode::Block { statements, expr } => {
self.visit_block_impl(ast, idx, statements, expr)
}
AstNode::Constant { ty, value } => self.visit_constant_impl(ast, idx, ty, value),
AstNode::ExprStmt { expr } => self.visit_expr_stmt_impl(ast, idx, expr),
AstNode::ReturnStmt => self.visit_return_stmt_impl(ast, idx),
AstNode::ReturnExprStmt { expr } => self.visit_return_expr_stmt_impl(ast, idx, expr),
AstNode::VarDecl { name, ty } => self.visit_var_decl_impl(ast, idx, name, ty),
AstNode::MutVarDecl { name, ty } => self.visit_mut_var_decl_impl(ast, idx, name, ty),
AstNode::VarDeclAssignment { name, expr, ty } => {
self.visit_var_decl_assignment_impl(ast, idx, name, expr, ty)
}
AstNode::MutVarDeclAssignment { name, expr, ty } => {
self.visit_mut_var_decl_assignment_impl(ast, idx, name, expr, ty)
}
AstNode::GlobalDecl { name, expr, ty } => {
self.visit_global_decl_impl(ast, idx, name, expr, ty)
}
AstNode::StructDecl {
name,
flags,
field_names,
field_types,
} => self.visit_struct_decl_impl(ast, idx, name, flags, field_names, field_types),
AstNode::StructDeclInterned { name, ty } => {
self.visit_struct_decl_interned_impl(ast, idx, name, ty)
}
AstNode::FieldDecl { name, ty } => self.visit_field_decl_impl(ast, idx, name, ty),
AstNode::DeclRef { decl } => self.visit_decl_ref_impl(ast, idx, decl),
AstNode::DeclRefUnresolved { scope, name } => {
self.visit_decl_ref_unresolved_impl(ast, idx, scope, name)
}
AstNode::InternedType { intern } => self.visit_interned_type_impl(ast, idx, intern),
AstNode::TypeDeclRef { decl } => self.visit_type_decl_ref_impl(ast, idx, decl),
AstNode::TypeDeclRefUnresolved { scope, name } => {
self.visit_type_decl_ref_unresolved_impl(ast, idx, scope, name)
}
AstNode::PointerType { ty, flags } => self.visit_pointer_type_impl(ast, idx, ty, flags),
AstNode::ArrayType { length, pointer } => {
self.visit_array_type_impl(ast, idx, length, pointer)
}
AstNode::CallExpr {
func,
argument_list,
} => self.visit_call_expr_impl(ast, idx, func, argument_list),
AstNode::FieldAccess { field_name, expr } => {
self.visit_field_access_impl(ast, idx, field_name, expr)
}
AstNode::ArgumentList { arguments } => {
self.visit_argument_list_impl(ast, idx, arguments)
}
AstNode::Argument { expr, name } => self.visit_argument_impl(ast, idx, expr, name),
AstNode::ExplicitCast { expr, ty } => self.visit_explicit_cast_impl(ast, idx, expr, ty),
AstNode::Deref { expr } => self.visit_deref_impl(ast, idx, expr),
AstNode::AddressOf { expr } => self.visit_address_of_impl(ast, idx, expr),
AstNode::Not { expr } => self.visit_not_impl(ast, idx, expr),
AstNode::Negate { expr } => self.visit_negate_impl(ast, idx, expr),
AstNode::PlaceToValueConversion { expr } => {
self.visit_place_to_value_conversion_impl(ast, idx, expr)
}
AstNode::ValueToPlaceConversion { expr } => {
self.visit_value_to_place_conversion_impl(ast, idx, expr)
}
AstNode::Or { lhs, rhs } => self.visit_or_impl(ast, idx, lhs, rhs),
AstNode::And { lhs, rhs } => self.visit_and_impl(ast, idx, lhs, rhs),
AstNode::BitOr { lhs, rhs } => self.visit_bit_or_impl(ast, idx, lhs, rhs),
AstNode::BitXOr { lhs, rhs } => self.visit_bit_x_or_impl(ast, idx, lhs, rhs),
AstNode::BitAnd { lhs, rhs } => self.visit_bit_and_impl(ast, idx, lhs, rhs),
AstNode::Eq { lhs, rhs } => self.visit_eq_impl(ast, idx, lhs, rhs),
AstNode::NEq { lhs, rhs } => self.visit_n_eq_impl(ast, idx, lhs, rhs),
AstNode::Lt { lhs, rhs } => self.visit_lt_impl(ast, idx, lhs, rhs),
AstNode::Gt { lhs, rhs } => self.visit_gt_impl(ast, idx, lhs, rhs),
AstNode::Le { lhs, rhs } => self.visit_le_impl(ast, idx, lhs, rhs),
AstNode::Ge { lhs, rhs } => self.visit_ge_impl(ast, idx, lhs, rhs),
AstNode::Shl { lhs, rhs } => self.visit_shl_impl(ast, idx, lhs, rhs),
AstNode::Shr { lhs, rhs } => self.visit_shr_impl(ast, idx, lhs, rhs),
AstNode::Add { lhs, rhs } => self.visit_add_impl(ast, idx, lhs, rhs),
AstNode::Sub { lhs, rhs } => self.visit_sub_impl(ast, idx, lhs, rhs),
AstNode::Mul { lhs, rhs } => self.visit_mul_impl(ast, idx, lhs, rhs),
AstNode::Div { lhs, rhs } => self.visit_div_impl(ast, idx, lhs, rhs),
AstNode::Rem { lhs, rhs } => self.visit_rem_impl(ast, idx, lhs, rhs),
AstNode::Assign { lhs, rhs } => self.visit_assign_impl(ast, idx, lhs, rhs),
AstNode::SubscriptExpr { lhs, index: rhs } => {
self.visit_subscript_expr_impl(ast, idx, lhs, rhs)
}
AstNode::IfExpr { cond, body } => self.visit_if_expr_impl(ast, idx, cond, body),
AstNode::IfElseExpr { cond, a, b } => {
self.visit_if_else_expr_impl(ast, idx, cond, a, b)
}
AstNode::Error { err } => self.visit_error_impl(ast, idx, err),
AstNode::Undefined => self.visit_undefined_impl(ast, idx),
AstNode::Root { .. } => unreachable!(),
}
}
}

View file

@ -55,7 +55,7 @@ fn main() {
println!("AST:\n{buf}");
}
"ast2" => {
let mut tree2 = compiler::ast2::parser::Parser::new();
let mut tree2 = compiler::ast2::ast_gen::Parser::new();
tree2.parse(tokens.iter());
eprintln!("{tree2:#?}");
println!("AST (new):\n{tree2}");

View file

@ -72,7 +72,9 @@ pub fn is_oct_digit(ch: char) -> bool {
}
pub fn is_hex_digit(ch: char) -> bool {
('0'..='9').contains(&ch) || ('a'..='f').contains(&ch) || ('A'..='F').contains(&ch)
('0'..='9').contains(&ch)
|| ('a'..='f').contains(&ch)
|| ('A'..='F').contains(&ch)
}
/// Trait for only yielding the next item in the Iterator if it tests true for some predicate
@ -174,19 +176,3 @@ pub fn from_lo_hi_dwords(lo: u32, hi: u32) -> u64 {
pub fn into_lo_hi_dwords(qword: u64) -> (u32, u32) {
(qword as u32, (qword >> 32) as u32)
}
pub fn u32_as_u16_slice(value: &u32) -> &[u16; 2] {
// SAFETY: This is safe because u32 is guaranteed to be 4 bytes and
// we are creating a slice of 2 u16s which is also 4 bytes.
unsafe { &*(value as *const u32 as *const [u16; 2]) }
}
// we can't transform any &[u16; 2] into a &u32 because of alignment guarantees
pub fn u32_from_u16_slice(slice: &[u16; 2]) -> u32 {
let mut out = 0u32;
unsafe {
core::ptr::copy_nonoverlapping(slice.as_ptr(), &raw mut out as *mut u16, 2);
}
out
}

View file

@ -12,7 +12,10 @@ pub mod bigint {
pub struct BigInt(Vec<u32>);
impl BigInt {
pub fn parse_digits<C: IntoIterator<Item = char>>(text: C, radix: Radix) -> BigInt {
pub fn parse_digits<C: IntoIterator<Item = char>>(
text: C,
radix: Radix,
) -> BigInt {
Self(parse_bigint(text.into_iter(), radix))
}
pub fn from_u32(v: u32) -> BigInt {
@ -237,9 +240,13 @@ pub mod bigint {
fn sub(mut self, rhs: Self) -> Self::Output {
if self.0.len() < rhs.0.len() {
println!("extending self by {} zeroes", rhs.0.len() - self.0.len());
self.0
.extend(core::iter::repeat(0).take(rhs.0.len() - self.0.len()));
println!(
"extending self by {} zeroes",
rhs.0.len() - self.0.len()
);
self.0.extend(
core::iter::repeat(0).take(rhs.0.len() - self.0.len()),
);
println!("self: {self:?}");
}
sub_bigint(&mut self.0, &rhs.0);
@ -497,7 +504,10 @@ pub mod bigint {
_ => {
if scalar.is_power_of_two() {
lhs.push(0);
shl_bitint(lhs.as_mut_slice(), scalar.trailing_zeros() as usize);
shl_bitint(
lhs.as_mut_slice(),
scalar.trailing_zeros() as usize,
);
} else {
let mut carry = 0;
for a in lhs.iter_mut() {
@ -570,7 +580,7 @@ pub mod bigint {
fn trailing_zeros(lhs: &[u32]) -> usize {
lhs.iter()
.enumerate()
.find(|&(_, &c)| c != 0)
.find(|(_, &c)| c != 0)
.map(|(i, &c)| i * u32::BITS as usize + c.trailing_zeros() as usize)
.unwrap_or(0)
}
@ -583,7 +593,10 @@ pub mod bigint {
#[allow(dead_code)]
/// divident must be at least as wide as divisor
/// returns (quotient, remainder)
pub fn div_rem_bigint_ref(divident: &BigInt, divisor: &BigInt) -> (BigInt, BigInt) {
pub fn div_rem_bigint_ref(
divident: &BigInt,
divisor: &BigInt,
) -> (BigInt, BigInt) {
if bigint_is_zero(&divisor.0) {
panic!("divide by zero!");
}
@ -601,7 +614,8 @@ pub mod bigint {
if divisor.is_power_of_two() {
let exp = divisor.trailing_zeros();
let (div, rem) = divident.0.split_at(exp.div_floor(u32::BITS as usize));
let (div, rem) =
divident.0.split_at(exp.div_floor(u32::BITS as usize));
let (mut div, mut rem) = (div.to_vec(), rem.to_vec());
shr_bitint(&mut div, exp % u32::BITS as usize);
@ -627,7 +641,10 @@ pub mod bigint {
if shift == 0 {
div_rem_core(divident.clone(), &divisor.0)
} else {
let (q, r) = div_rem_core(divident.clone() << shift, &(divisor.clone() << shift).0);
let (q, r) = div_rem_core(
divident.clone() << shift,
&(divisor.clone() << shift).0,
);
(q, r >> shift)
}
@ -636,7 +653,10 @@ pub mod bigint {
#[allow(dead_code)]
/// divident must be at least as wide as divisor
/// returns (quotient, remainder)
pub fn div_rem_bigint(divident: BigInt, divisor: BigInt) -> (BigInt, BigInt) {
pub fn div_rem_bigint(
divident: BigInt,
divisor: BigInt,
) -> (BigInt, BigInt) {
let divident = divident.normalised();
let mut divisor = divisor.normalised();
@ -657,7 +677,8 @@ pub mod bigint {
if divisor.is_power_of_two() {
let exp = divisor.trailing_zeros();
let (div, rem) = divident.0.split_at(exp.div_floor(u32::BITS as usize));
let (div, rem) =
divident.0.split_at(exp.div_floor(u32::BITS as usize));
let (mut div, mut rem) = (div.to_vec(), rem.to_vec());
shr_bitint(&mut div, exp % u32::BITS as usize);
@ -789,7 +810,9 @@ pub mod bigint {
// q0 is too large if:
// [a2,a1,a0] < q0 * [b1,b0]
// (r << BITS) + a2 < q0 * b1
while r <= u32::MAX as u64 && from_lo_hi_dwords(r as u32, a2) < q0 as u64 * b1 as u64 {
while r <= u32::MAX as u64
&& from_lo_hi_dwords(r as u32, a2) < q0 as u64 * b1 as u64
{
q0 -= 1;
r += b0 as u64;
}
@ -1018,7 +1041,10 @@ pub mod bigint {
carry
}
pub fn parse_bigint(text: impl Iterator<Item = char>, radix: Radix) -> Vec<u32> {
pub fn parse_bigint(
text: impl Iterator<Item = char>,
radix: Radix,
) -> Vec<u32> {
let digits = text
.filter_map(|c| match c {
'_' => None,
@ -1086,14 +1112,20 @@ pub mod bigint {
#[test]
fn parse() {
let bigint = BigInt::parse_digits("2_cafe_babe_dead_beef".chars(), Radix::Hex);
let bigint = BigInt::parse_digits(
"2_cafe_babe_dead_beef".chars(),
Radix::Hex,
);
println!("{:#x?}", bigint);
let bigint = BigInt::parse_digits("f".chars(), Radix::Hex);
println!("{:#x?}", bigint);
}
#[test]
fn add() {
let a = BigInt::parse_digits("2_0000_0000_0000_0000".chars(), Radix::Hex);
let a = BigInt::parse_digits(
"2_0000_0000_0000_0000".chars(),
Radix::Hex,
);
println!("{:#x?}", a);
let b = BigInt::parse_digits("cafebabe".chars(), Radix::Hex);
println!("{:#x?}", b);
@ -1111,7 +1143,10 @@ pub mod bigint {
}
#[test]
fn overflowing_sub() {
let a = BigInt::parse_digits("2_0000_0000_0000_0000".chars(), Radix::Hex);
let a = BigInt::parse_digits(
"2_0000_0000_0000_0000".chars(),
Radix::Hex,
);
println!("{:#x?}", a);
let b = BigInt::parse_digits("ffff_ffff".chars(), Radix::Hex);
println!("{:#x?}", b);
@ -1120,7 +1155,8 @@ pub mod bigint {
}
#[test]
fn shr() {
let mut a = BigInt::parse_digits("cafe_babe_0000".chars(), Radix::Hex);
let mut a =
BigInt::parse_digits("cafe_babe_0000".chars(), Radix::Hex);
print!("{:0>8x?} >> 32 ", a);
shr_bitint(&mut a.0, 32);
println!("{:0>8x?}", a);
@ -1152,7 +1188,9 @@ pub mod bigint {
pub mod bigsint {
use std::{
cmp::Ordering,
ops::{Add, AddAssign, Div, Mul, Neg, Not, Rem, Shl, Shr, Sub, SubAssign},
ops::{
Add, AddAssign, Div, Mul, Neg, Not, Rem, Shl, Shr, Sub, SubAssign,
},
};
use super::bigint::{self, *};
@ -1295,11 +1333,13 @@ pub mod bigsint {
match (self.sign, rhs.sign) {
(_, Sign::None) => self,
(Sign::None, _) => rhs,
(Sign::Positive, Sign::Positive) | (Sign::Negative, Sign::Negative) => Self {
(Sign::Positive, Sign::Positive)
| (Sign::Negative, Sign::Negative) => Self {
sign: self.sign,
bigint: self.bigint + rhs.bigint,
},
(Sign::Positive, Sign::Negative) | (Sign::Negative, Sign::Positive) => {
(Sign::Positive, Sign::Negative)
| (Sign::Negative, Sign::Positive) => {
match self.bigint.cmp(&rhs.bigint) {
Ordering::Less => Self {
sign: rhs.sign,
@ -1321,11 +1361,13 @@ pub mod bigsint {
fn add(self, rhs: u32) -> Self::Output {
match self.sign {
Sign::Negative => match self.bigint.partial_cmp(&rhs).unwrap() {
Ordering::Less => Self::positive(rhs - self.bigint),
Ordering::Equal => Self::zero(),
Ordering::Greater => -Self::positive(self.bigint - rhs),
},
Sign::Negative => {
match self.bigint.partial_cmp(&rhs).unwrap() {
Ordering::Less => Self::positive(rhs - self.bigint),
Ordering::Equal => Self::zero(),
Ordering::Greater => -Self::positive(self.bigint - rhs),
}
}
Sign::None => Self::from_u32(rhs),
Sign::Positive => Self::positive(self.bigint + rhs),
}
@ -1337,11 +1379,13 @@ pub mod bigsint {
fn add(self, rhs: u64) -> Self::Output {
match self.sign {
Sign::Negative => match self.bigint.partial_cmp(&rhs).unwrap() {
Ordering::Less => Self::positive(rhs - self.bigint),
Ordering::Equal => Self::zero(),
Ordering::Greater => -Self::positive(self.bigint - rhs),
},
Sign::Negative => {
match self.bigint.partial_cmp(&rhs).unwrap() {
Ordering::Less => Self::positive(rhs - self.bigint),
Ordering::Equal => Self::zero(),
Ordering::Greater => -Self::positive(self.bigint - rhs),
}
}
Sign::None => Self::from_u64(rhs),
Sign::Positive => Self::positive(self.bigint + rhs),
}
@ -1362,11 +1406,13 @@ pub mod bigsint {
match (self.sign, rhs.sign) {
(_, Sign::None) => self,
(Sign::None, _) => -rhs,
(Sign::Positive, Sign::Negative) | (Sign::Negative, Sign::Positive) => Self {
(Sign::Positive, Sign::Negative)
| (Sign::Negative, Sign::Positive) => Self {
sign: self.sign,
bigint: self.bigint + rhs.bigint,
},
(Sign::Positive, Sign::Positive) | (Sign::Negative, Sign::Negative) => {
(Sign::Positive, Sign::Positive)
| (Sign::Negative, Sign::Negative) => {
match self.bigint.cmp(&rhs.bigint) {
Ordering::Less => Self {
sign: -self.sign,
@ -1470,7 +1516,11 @@ pub mod bigsint {
bigint: r,
};
if rhs.is_negative() { (-q, r) } else { (q, r) }
if rhs.is_negative() {
(-q, r)
} else {
(q, r)
}
}
fn shr_rounding(lhs: &BigSInt, shift: usize) -> bool {
@ -1498,7 +1548,7 @@ use std::{
};
use num_bigint::{BigInt, BigUint, Sign};
use num_traits::{ToBytes, cast::ToPrimitive};
use num_traits::{cast::ToPrimitive, ToBytes};
use crate::ast::{FloatingType, IntegralType, Type};
@ -1535,14 +1585,20 @@ impl ComptimeInt {
pub fn add(self, other: Self) -> Result<Self> {
let (a, b) = self.coalesce(other)?;
match (a, b) {
(ComptimeInt::Native { bits: a, ty }, ComptimeInt::Native { bits: b, .. }) => {
(
ComptimeInt::Native { bits: a, ty },
ComptimeInt::Native { bits: b, .. },
) => {
let bits = a.checked_add(b).ok_or(Error::IntegerOverflow)?;
if bits & !ty.u128_bitmask() != 0 {
return Err(Error::IntegerOverflow);
}
Ok(Self::Native { bits, ty })
}
(ComptimeInt::BigInt { bits: a, ty }, ComptimeInt::BigInt { bits: b, .. }) => {
(
ComptimeInt::BigInt { bits: a, ty },
ComptimeInt::BigInt { bits: b, .. },
) => {
let width = ty.bits - ty.signed as u16;
let bits = a + b;
if bits.bits() > width as u64 {
@ -1551,7 +1607,9 @@ impl ComptimeInt {
Ok(Self::BigInt { bits, ty })
}
}
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => Ok(Self::Comptime(a + b)),
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => {
Ok(Self::Comptime(a + b))
}
_ => {
unreachable!()
}
@ -1561,14 +1619,20 @@ impl ComptimeInt {
pub fn sub(self, other: Self) -> Result<Self> {
let (a, b) = self.coalesce(other)?;
match (a, b) {
(ComptimeInt::Native { bits: a, ty }, ComptimeInt::Native { bits: b, .. }) => {
(
ComptimeInt::Native { bits: a, ty },
ComptimeInt::Native { bits: b, .. },
) => {
let bits = a.checked_sub(b).ok_or(Error::IntegerOverflow)?;
if bits & !ty.u128_bitmask() != 0 {
return Err(Error::IntegerOverflow);
}
Ok(Self::Native { bits, ty })
}
(ComptimeInt::BigInt { bits: a, ty }, ComptimeInt::BigInt { bits: b, .. }) => {
(
ComptimeInt::BigInt { bits: a, ty },
ComptimeInt::BigInt { bits: b, .. },
) => {
let width = ty.bits - ty.signed as u16;
let bits = a - b;
if bits.bits() > width as u64 {
@ -1577,7 +1641,9 @@ impl ComptimeInt {
Ok(Self::BigInt { bits, ty })
}
}
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => Ok(Self::Comptime(a - b)),
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => {
Ok(Self::Comptime(a - b))
}
_ => {
unreachable!()
}
@ -1587,14 +1653,20 @@ impl ComptimeInt {
pub fn mul(self, other: Self) -> Result<Self> {
let (a, b) = self.coalesce(other)?;
match (a, b) {
(ComptimeInt::Native { bits: a, ty }, ComptimeInt::Native { bits: b, .. }) => {
(
ComptimeInt::Native { bits: a, ty },
ComptimeInt::Native { bits: b, .. },
) => {
let bits = a.checked_mul(b).ok_or(Error::IntegerOverflow)?;
if bits & !ty.u128_bitmask() != 0 {
return Err(Error::IntegerOverflow);
}
Ok(Self::Native { bits, ty })
}
(ComptimeInt::BigInt { bits: a, ty }, ComptimeInt::BigInt { bits: b, .. }) => {
(
ComptimeInt::BigInt { bits: a, ty },
ComptimeInt::BigInt { bits: b, .. },
) => {
let width = ty.bits - ty.signed as u16;
let bits = a * b;
if bits.bits() > width as u64 {
@ -1603,7 +1675,9 @@ impl ComptimeInt {
Ok(Self::BigInt { bits, ty })
}
}
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => Ok(Self::Comptime(a * b)),
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => {
Ok(Self::Comptime(a * b))
}
_ => {
unreachable!()
}
@ -1613,14 +1687,20 @@ impl ComptimeInt {
pub fn div(self, other: Self) -> Result<Self> {
let (a, b) = self.coalesce(other)?;
match (a, b) {
(ComptimeInt::Native { bits: a, ty }, ComptimeInt::Native { bits: b, .. }) => {
(
ComptimeInt::Native { bits: a, ty },
ComptimeInt::Native { bits: b, .. },
) => {
let bits = a.checked_div(b).ok_or(Error::IntegerOverflow)?;
if bits & !ty.u128_bitmask() != 0 {
return Err(Error::IntegerOverflow);
}
Ok(Self::Native { bits, ty })
}
(ComptimeInt::BigInt { bits: a, ty }, ComptimeInt::BigInt { bits: b, .. }) => {
(
ComptimeInt::BigInt { bits: a, ty },
ComptimeInt::BigInt { bits: b, .. },
) => {
let width = ty.bits - ty.signed as u16;
let bits = a / b;
if bits.bits() > width as u64 {
@ -1629,7 +1709,9 @@ impl ComptimeInt {
Ok(Self::BigInt { bits, ty })
}
}
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => Ok(Self::Comptime(a / b)),
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => {
Ok(Self::Comptime(a / b))
}
_ => {
unreachable!()
}
@ -1639,14 +1721,20 @@ impl ComptimeInt {
pub fn rem(self, other: Self) -> Result<Self> {
let (a, b) = self.coalesce(other)?;
match (a, b) {
(ComptimeInt::Native { bits: a, ty }, ComptimeInt::Native { bits: b, .. }) => {
(
ComptimeInt::Native { bits: a, ty },
ComptimeInt::Native { bits: b, .. },
) => {
let bits = a.checked_rem(b).ok_or(Error::IntegerOverflow)?;
if bits & !ty.u128_bitmask() != 0 {
return Err(Error::IntegerOverflow);
}
Ok(Self::Native { bits, ty })
}
(ComptimeInt::BigInt { bits: a, ty }, ComptimeInt::BigInt { bits: b, .. }) => {
(
ComptimeInt::BigInt { bits: a, ty },
ComptimeInt::BigInt { bits: b, .. },
) => {
let width = ty.bits - ty.signed as u16;
let bits = a % b;
if bits.bits() > width as u64 {
@ -1655,7 +1743,9 @@ impl ComptimeInt {
Ok(Self::BigInt { bits, ty })
}
}
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => Ok(Self::Comptime(a % b)),
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => {
Ok(Self::Comptime(a % b))
}
_ => {
unreachable!()
}
@ -1665,15 +1755,23 @@ impl ComptimeInt {
pub fn bitand(self, other: Self) -> Result<Self> {
let (a, b) = self.coalesce(other)?;
match (a, b) {
(ComptimeInt::Native { bits: a, ty }, ComptimeInt::Native { bits: b, .. }) => {
(
ComptimeInt::Native { bits: a, ty },
ComptimeInt::Native { bits: b, .. },
) => {
let bits = a.bitand(b);
Ok(Self::Native { bits, ty })
}
(ComptimeInt::BigInt { bits: a, ty }, ComptimeInt::BigInt { bits: b, .. }) => {
(
ComptimeInt::BigInt { bits: a, ty },
ComptimeInt::BigInt { bits: b, .. },
) => {
let bits = a & b;
Ok(Self::BigInt { bits, ty })
}
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => Ok(Self::Comptime(a & b)),
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => {
Ok(Self::Comptime(a & b))
}
_ => {
unreachable!()
}
@ -1683,15 +1781,23 @@ impl ComptimeInt {
pub fn bitor(self, other: Self) -> Result<Self> {
let (a, b) = self.coalesce(other)?;
match (a, b) {
(ComptimeInt::Native { bits: a, ty }, ComptimeInt::Native { bits: b, .. }) => {
(
ComptimeInt::Native { bits: a, ty },
ComptimeInt::Native { bits: b, .. },
) => {
let bits = a.bitor(b);
Ok(Self::Native { bits, ty })
}
(ComptimeInt::BigInt { bits: a, ty }, ComptimeInt::BigInt { bits: b, .. }) => {
(
ComptimeInt::BigInt { bits: a, ty },
ComptimeInt::BigInt { bits: b, .. },
) => {
let bits = a | b;
Ok(Self::BigInt { bits, ty })
}
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => Ok(Self::Comptime(a | b)),
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => {
Ok(Self::Comptime(a | b))
}
_ => {
unreachable!()
}
@ -1701,15 +1807,23 @@ impl ComptimeInt {
pub fn bitxor(self, other: Self) -> Result<Self> {
let (a, b) = self.coalesce(other)?;
match (a, b) {
(ComptimeInt::Native { bits: a, ty }, ComptimeInt::Native { bits: b, .. }) => {
(
ComptimeInt::Native { bits: a, ty },
ComptimeInt::Native { bits: b, .. },
) => {
let bits = a.bitxor(b);
Ok(Self::Native { bits, ty })
}
(ComptimeInt::BigInt { bits: a, ty }, ComptimeInt::BigInt { bits: b, .. }) => {
(
ComptimeInt::BigInt { bits: a, ty },
ComptimeInt::BigInt { bits: b, .. },
) => {
let bits = a ^ b;
Ok(Self::BigInt { bits, ty })
}
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => Ok(Self::Comptime(a ^ b)),
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => {
Ok(Self::Comptime(a ^ b))
}
_ => {
unreachable!()
}
@ -1718,8 +1832,14 @@ impl ComptimeInt {
pub fn cmp(self, other: Self) -> Result<Ordering> {
let (a, b) = self.coalesce(other)?;
let ord = match (a, b) {
(ComptimeInt::Native { bits: a, .. }, ComptimeInt::Native { bits: b, .. }) => a.cmp(&b),
(ComptimeInt::BigInt { bits: a, .. }, ComptimeInt::BigInt { bits: b, .. }) => a.cmp(&b),
(
ComptimeInt::Native { bits: a, .. },
ComptimeInt::Native { bits: b, .. },
) => a.cmp(&b),
(
ComptimeInt::BigInt { bits: a, .. },
ComptimeInt::BigInt { bits: b, .. },
) => a.cmp(&b),
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => a.cmp(&b),
_ => {
unreachable!()
@ -1737,11 +1857,13 @@ impl ComptimeInt {
let bits = if ty.signed {
(bits as i128)
.checked_shl(shift)
.ok_or(Error::IntegerOverflow)? as u128
.ok_or(Error::IntegerOverflow)?
as u128
} else {
(bits as u128)
.checked_shl(shift)
.ok_or(Error::IntegerOverflow)? as u128
.ok_or(Error::IntegerOverflow)?
as u128
} & ty.u128_bitmask();
Ok(Self::Native { bits, ty })
@ -1766,11 +1888,13 @@ impl ComptimeInt {
let bits = if ty.signed {
(bits as i128)
.checked_shr(shift)
.ok_or(Error::IntegerOverflow)? as u128
.ok_or(Error::IntegerOverflow)?
as u128
} else {
(bits as u128)
.checked_shr(shift)
.ok_or(Error::IntegerOverflow)? as u128
.ok_or(Error::IntegerOverflow)?
as u128
};
Ok(Self::Native { bits, ty })
@ -1789,7 +1913,9 @@ impl ComptimeInt {
if ty.signed {
return Err(Error::UnsignedNegation);
}
let bits = (a as i128).checked_neg().ok_or(Error::IntegerOverflow)? as u128;
let bits =
(a as i128).checked_neg().ok_or(Error::IntegerOverflow)?
as u128;
if bits & !ty.u128_bitmask() != 0 {
return Err(Error::IntegerOverflow);
@ -1807,7 +1933,9 @@ impl ComptimeInt {
bits: !bits | ty.u128_bitmask(),
ty,
}),
ComptimeInt::BigInt { bits, ty } => Ok(Self::BigInt { bits: !bits, ty }),
ComptimeInt::BigInt { bits, ty } => {
Ok(Self::BigInt { bits: !bits, ty })
}
ComptimeInt::Comptime(bigint) => Ok(Self::Comptime(!bigint)),
}
}
@ -1823,8 +1951,14 @@ impl ComptimeInt {
fn coalesce(self, other: Self) -> Result<(ComptimeInt, ComptimeInt)> {
match (self, other) {
(lhs @ ComptimeInt::Native { ty: a_ty, .. }, ComptimeInt::Comptime(b))
| (lhs @ ComptimeInt::Native { ty: a_ty, .. }, ComptimeInt::BigInt { bits: b, .. }) => {
(
lhs @ ComptimeInt::Native { ty: a_ty, .. },
ComptimeInt::Comptime(b),
)
| (
lhs @ ComptimeInt::Native { ty: a_ty, .. },
ComptimeInt::BigInt { bits: b, .. },
) => {
let b_signed = b.sign() == Sign::Minus;
if !a_ty.signed && b_signed {
return Err(Error::IncompatibleTypes);
@ -1841,8 +1975,14 @@ impl ComptimeInt {
};
Ok((lhs, Self::Native { bits: b, ty: a_ty }))
}
(ComptimeInt::Comptime(b), rhs @ ComptimeInt::Native { ty: a_ty, .. })
| (ComptimeInt::BigInt { bits: b, .. }, rhs @ ComptimeInt::Native { ty: a_ty, .. }) => {
(
ComptimeInt::Comptime(b),
rhs @ ComptimeInt::Native { ty: a_ty, .. },
)
| (
ComptimeInt::BigInt { bits: b, .. },
rhs @ ComptimeInt::Native { ty: a_ty, .. },
) => {
let b_signed = b.sign() == Sign::Minus;
if !a_ty.signed && b_signed {
return Err(Error::IncompatibleTypes);
@ -1859,7 +1999,10 @@ impl ComptimeInt {
};
Ok((Self::Native { bits: b, ty: a_ty }, rhs))
}
(lhs @ ComptimeInt::BigInt { ty, .. }, ComptimeInt::Comptime(b)) => {
(
lhs @ ComptimeInt::BigInt { ty, .. },
ComptimeInt::Comptime(b),
) => {
let b_signed = b.sign() == Sign::Minus;
if !ty.signed && b_signed {
return Err(Error::IncompatibleTypes);
@ -1871,7 +2014,10 @@ impl ComptimeInt {
}
Ok((lhs, Self::BigInt { bits: b, ty }))
}
(ComptimeInt::Comptime(b), rhs @ ComptimeInt::BigInt { ty, .. }) => {
(
ComptimeInt::Comptime(b),
rhs @ ComptimeInt::BigInt { ty, .. },
) => {
let b_signed = b.sign() == Sign::Minus;
if !ty.signed && b_signed {
return Err(Error::IncompatibleTypes);
@ -1883,14 +2029,20 @@ impl ComptimeInt {
}
Ok((Self::BigInt { bits: b, ty }, rhs))
}
(lhs @ ComptimeInt::Native { ty: a, .. }, rhs @ ComptimeInt::Native { ty: b, .. }) => {
(
lhs @ ComptimeInt::Native { ty: a, .. },
rhs @ ComptimeInt::Native { ty: b, .. },
) => {
if a == b {
Ok((lhs, rhs))
} else {
Err(Error::IncompatibleTypes)
}
}
(lhs @ ComptimeInt::BigInt { ty: a, .. }, rhs @ ComptimeInt::BigInt { ty: b, .. }) => {
(
lhs @ ComptimeInt::BigInt { ty: a, .. },
rhs @ ComptimeInt::BigInt { ty: b, .. },
) => {
if a == b {
Ok((lhs, rhs))
} else {
@ -1911,36 +2063,56 @@ pub enum ComptimeFloat {
impl ComptimeFloat {
pub fn add(self, other: Self) -> Result<ComptimeFloat> {
match (self, other) {
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => Ok(Self::Binary32(a + b)),
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => Ok(Self::Binary64(a + b)),
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => {
Ok(Self::Binary32(a + b))
}
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => {
Ok(Self::Binary64(a + b))
}
_ => Err(Error::IncompatibleTypes),
}
}
pub fn sub(self, other: Self) -> Result<ComptimeFloat> {
match (self, other) {
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => Ok(Self::Binary32(a - b)),
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => Ok(Self::Binary64(a - b)),
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => {
Ok(Self::Binary32(a - b))
}
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => {
Ok(Self::Binary64(a - b))
}
_ => Err(Error::IncompatibleTypes),
}
}
pub fn mul(self, other: Self) -> Result<ComptimeFloat> {
match (self, other) {
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => Ok(Self::Binary32(a * b)),
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => Ok(Self::Binary64(a * b)),
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => {
Ok(Self::Binary32(a * b))
}
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => {
Ok(Self::Binary64(a * b))
}
_ => Err(Error::IncompatibleTypes),
}
}
pub fn div(self, other: Self) -> Result<ComptimeFloat> {
match (self, other) {
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => Ok(Self::Binary32(a / b)),
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => Ok(Self::Binary64(a / b)),
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => {
Ok(Self::Binary32(a / b))
}
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => {
Ok(Self::Binary64(a / b))
}
_ => Err(Error::IncompatibleTypes),
}
}
pub fn rem(self, other: Self) -> Result<ComptimeFloat> {
match (self, other) {
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => Ok(Self::Binary32(a % b)),
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => Ok(Self::Binary64(a % b)),
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => {
Ok(Self::Binary32(a % b))
}
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => {
Ok(Self::Binary64(a % b))
}
_ => Err(Error::IncompatibleTypes),
}
}
@ -1952,8 +2124,12 @@ impl ComptimeFloat {
}
pub fn cmp(self, other: Self) -> Result<Ordering> {
let ord = match (self, other) {
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => a.partial_cmp(&b),
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => a.partial_cmp(&b),
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => {
a.partial_cmp(&b)
}
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => {
a.partial_cmp(&b)
}
_ => {
return Err(Error::IncompatibleTypes);
}
@ -2105,7 +2281,9 @@ impl ComptimeNumber {
// (ComptimeNumber::Floating(a), ComptimeNumber::Floating(b)) => {
// Ok(Self::Floating(a.sub(b)?))
// }
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => Ok(Self::Bool(a.bitand(b))),
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => {
Ok(Self::Bool(a.bitand(b)))
}
_ => Err(Error::IncompatibleTypes),
}
}
@ -2117,7 +2295,9 @@ impl ComptimeNumber {
// (ComptimeNumber::Floating(a), ComptimeNumber::Floating(b)) => {
// Ok(Self::Floating(a.bitor(b)?))
// }
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => Ok(Self::Bool(a.bitor(b))),
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => {
Ok(Self::Bool(a.bitor(b)))
}
_ => Err(Error::IncompatibleTypes),
}
}
@ -2129,7 +2309,9 @@ impl ComptimeNumber {
// (ComptimeNumber::Floating(a), ComptimeNumber::Floating(b)) => {
// Ok(Self::Floating(a.bitxor(b)?))
// }
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => Ok(Self::Bool(a.bitxor(b))),
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => {
Ok(Self::Bool(a.bitxor(b)))
}
_ => Err(Error::IncompatibleTypes),
}
}
@ -2165,7 +2347,9 @@ impl ComptimeNumber {
// (ComptimeNumber::Floating(a), ComptimeNumber::Floating(b)) => {
// Ok(Self::Floating(a.bitxor(b)?))
// }
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => Ok(Self::Bool(a || b)),
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => {
Ok(Self::Bool(a || b))
}
_ => Err(Error::IncompatibleTypes),
}
}
@ -2177,23 +2361,35 @@ impl ComptimeNumber {
// (ComptimeNumber::Floating(a), ComptimeNumber::Floating(b)) => {
// Ok(Self::Floating(a.bitxor(b)?))
// }
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => Ok(Self::Bool(a && b)),
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => {
Ok(Self::Bool(a && b))
}
_ => Err(Error::IncompatibleTypes),
}
}
pub fn eq(self, other: Self) -> Result<Self> {
match (self, other) {
(ComptimeNumber::Integral(a), ComptimeNumber::Integral(b)) => Ok(Self::Bool(a == b)),
(ComptimeNumber::Floating(a), ComptimeNumber::Floating(b)) => Ok(Self::Bool(a == b)),
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => Ok(Self::Bool(a == b)),
(ComptimeNumber::Integral(a), ComptimeNumber::Integral(b)) => {
Ok(Self::Bool(a == b))
}
(ComptimeNumber::Floating(a), ComptimeNumber::Floating(b)) => {
Ok(Self::Bool(a == b))
}
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => {
Ok(Self::Bool(a == b))
}
_ => Err(Error::IncompatibleTypes),
}
}
pub fn cmp(self, other: Self) -> Result<Ordering> {
let ord = match (self, other) {
(ComptimeNumber::Integral(a), ComptimeNumber::Integral(b)) => a.cmp(b)?,
(ComptimeNumber::Floating(a), ComptimeNumber::Floating(b)) => a.cmp(b)?,
(ComptimeNumber::Integral(a), ComptimeNumber::Integral(b)) => {
a.cmp(b)?
}
(ComptimeNumber::Floating(a), ComptimeNumber::Floating(b)) => {
a.cmp(b)?
}
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => a.cmp(&b),
_ => {
return Err(Error::IncompatibleTypes);
@ -2233,12 +2429,17 @@ impl ComptimeNumber {
match self {
ComptimeNumber::Integral(i) => match i {
ComptimeInt::Native { bits, .. } => Ok((bits != 0).into()),
ComptimeInt::Comptime(bits) | ComptimeInt::BigInt { bits, .. } => {
ComptimeInt::Comptime(bits)
| ComptimeInt::BigInt { bits, .. } => {
Ok((bits.sign() != Sign::NoSign).into())
}
},
ComptimeNumber::Floating(ComptimeFloat::Binary32(f)) => Ok((f != 0.0).into()),
ComptimeNumber::Floating(ComptimeFloat::Binary64(f)) => Ok((f != 0.0).into()),
ComptimeNumber::Floating(ComptimeFloat::Binary32(f)) => {
Ok((f != 0.0).into())
}
ComptimeNumber::Floating(ComptimeFloat::Binary64(f)) => {
Ok((f != 0.0).into())
}
a => Ok(a),
}
}
@ -2246,19 +2447,29 @@ impl ComptimeNumber {
pub fn into_int(self, ty: IntegralType) -> Result<Self> {
match self {
ComptimeNumber::Integral(i) => match i {
ComptimeInt::Native { bits, .. } => Ok((bits & ty.u128_bitmask(), ty).into()),
ComptimeInt::Comptime(bits) | ComptimeInt::BigInt { bits, .. } => {
let max = BigUint::from(2u32).pow((ty.bits - ty.signed as u16) as u32);
ComptimeInt::Native { bits, .. } => {
Ok((bits & ty.u128_bitmask(), ty).into())
}
ComptimeInt::Comptime(bits)
| ComptimeInt::BigInt { bits, .. } => {
let max = BigUint::from(2u32)
.pow((ty.bits - ty.signed as u16) as u32);
let (sign, data) = bits.into_parts();
let data = data.clamp(BigUint::ZERO, max);
Ok((BigInt::from_biguint(sign, data), ty).into())
}
},
ComptimeNumber::Bool(b) => Ok((b as u128 & ty.u128_bitmask(), ty).into()),
ComptimeNumber::Bool(b) => {
Ok((b as u128 & ty.u128_bitmask(), ty).into())
}
ComptimeNumber::Floating(f) => match f {
ComptimeFloat::Binary32(f) => Ok((f as u128 & ty.u128_bitmask(), ty).into()),
ComptimeFloat::Binary64(f) => Ok((f as u128 & ty.u128_bitmask(), ty).into()),
ComptimeFloat::Binary32(f) => {
Ok((f as u128 & ty.u128_bitmask(), ty).into())
}
ComptimeFloat::Binary64(f) => {
Ok((f as u128 & ty.u128_bitmask(), ty).into())
}
},
ComptimeNumber::Void => {
return Err(Error::VoidConversion);
@ -2269,7 +2480,8 @@ impl ComptimeNumber {
let f = match self {
ComptimeNumber::Integral(i) => match i {
ComptimeInt::Native { bits, .. } => bits as f64,
ComptimeInt::Comptime(bits) | ComptimeInt::BigInt { bits, .. } => {
ComptimeInt::Comptime(bits)
| ComptimeInt::BigInt { bits, .. } => {
bits.to_f64().unwrap_or(f64::NAN)
}
},

View file

@ -1,6 +1,8 @@
#![feature(
extract_if,
iter_advance_by,
box_into_inner,
hash_extract_if,
bigint_helper_methods,
map_try_insert,
iter_intersperse,
@ -8,10 +10,9 @@
int_roundings,
if_let_guard,
debug_closure_helpers,
box_vec_non_null,
macro_metavar_expr
)]
#![allow(unused_macros, unsafe_op_in_unsafe_fn)]
#![allow(unused_macros)]
pub mod asm;
pub mod ast;
@ -28,9 +29,6 @@ pub mod symbol_table;
pub mod tokens;
pub mod triples;
pub mod utils;
use utils::unit;
pub fn tokenize<'a>(
bytes: &'a [u8],
) -> Result<lexer::Tokenizer<'a>, (lexer::Tokenizer<'a>, Vec<lexer::TokenizeError>)> {

View file

@ -2,7 +2,6 @@ use itertools::Itertools;
use num_bigint::{BigInt, BigUint};
use crate::{
BitSize,
ast::{self, FloatingType, IntegralType, LetOrVar, Node, Tag, Type},
ast2::intern::{self, AMD64_POINTER_BITS},
common::NextIf,
@ -10,7 +9,8 @@ use crate::{
error::{AnalysisError, AnalysisErrorTag},
lexer::{Radix, TokenIterator},
symbol_table::{SymbolKind, SymbolTable},
tokens::{PRECEDENCE_MAP, Token},
tokens::{Token, PRECEDENCE_MAP},
BitSize,
};
#[derive(Debug, thiserror::Error)]
@ -1767,10 +1767,10 @@ impl Tree {
}
pub fn peer_type_of_nodes_unwrap(&self, lhs: Node, rhs: Node) -> Type {
self.peer_type_of_nodes(lhs, rhs).expect(&{
self.peer_type_of_nodes(lhs, rhs).expect({
let at = self.type_of_node(lhs);
let bt = self.type_of_node(rhs);
format!("incompatible types for %{lhs}({at}) and %{rhs}({bt})")
&format!("incompatible types for %{lhs}({at}) and %{rhs}({bt})")
})
}
@ -1844,10 +1844,10 @@ impl Tree {
} => {
let ty = match (explicit_type.as_ref(), assignment) {
(None, b) => self.type_of_node(*b),
(Some(a), b) => self.peer_type_of_nodes(*a, *b).expect(&{
(Some(a), b) => self.peer_type_of_nodes(*a, *b).expect({
let at = self.type_of_node(*a);
let bt = self.type_of_node(*b);
format!("incompatible types for %{a}({at}) and %{b}({bt})")
&format!("incompatible types for %{a}({at}) and %{b}({bt})")
}),
};
@ -1863,10 +1863,10 @@ impl Tree {
(None, None) => panic!("%{node}: no type specified?"),
(None, Some(b)) => self.type_of_node(*b),
(Some(a), None) => self.type_of_node(*a),
(Some(a), Some(b)) => self.peer_type_of_nodes(*a, *b).expect(&{
(Some(a), Some(b)) => self.peer_type_of_nodes(*a, *b).expect({
let at = self.type_of_node(*a);
let bt = self.type_of_node(*b);
format!("incompatible types for %{a}({at}) and %{b}({bt})")
&format!("incompatible types for %{a}({at}) and %{b}({bt})")
}),
};
@ -1888,10 +1888,10 @@ impl Tree {
| Tag::And { lhs, rhs }
| Tag::BitOr { lhs, rhs }
| Tag::BitAnd { lhs, rhs }
| Tag::BitXOr { lhs, rhs } => self.peer_type_of_nodes(*lhs, *rhs).expect(&{
| Tag::BitXOr { lhs, rhs } => self.peer_type_of_nodes(*lhs, *rhs).expect({
let at = self.type_of_node(*lhs);
let bt = self.type_of_node(*rhs);
format!("incompatible types for %{lhs}({at}) and %{rhs}({bt})")
&format!("incompatible types for %{lhs}({at}) and %{rhs}({bt})")
}),
Tag::Shl { lhs, .. } => self.type_of_node(*lhs),
Tag::Shr { lhs, .. } => self.type_of_node(*lhs),
@ -1906,11 +1906,11 @@ impl Tree {
Tag::IfExpr { .. } => Type::void(),
Tag::IfElseExpr {
body, else_expr, ..
} => self.peer_type_of_nodes(*body, *else_expr).expect(&{
} => self.peer_type_of_nodes(*body, *else_expr).expect({
let (lhs, rhs) = (body, else_expr);
let at = self.type_of_node(*lhs);
let bt = self.type_of_node(*rhs);
format!("incompatible types for %{lhs}({at}) and %{rhs}({bt})")
&format!("incompatible types for %{lhs}({at}) and %{rhs}({bt})")
}),
_ => Type::void(),
}

View file

@ -35,13 +35,25 @@ impl SymbolPath {
for node in self.0.iter().skip(1).rev() {
match tree.nodes.get_node(node.unwrap()) {
Tag::VarDecl { name, .. } => {
_ = write!(&mut buf, "V{}::", tree.get_ident_str(*name).unwrap());
_ = write!(
&mut buf,
"V{}::",
tree.get_ident_str(*name).unwrap()
);
}
Tag::GlobalDecl { name, .. } => {
_ = write!(&mut buf, "G{}::", tree.get_ident_str(*name).unwrap());
_ = write!(
&mut buf,
"G{}::",
tree.get_ident_str(*name).unwrap()
);
}
Tag::FunctionProto { name, .. } => {
_ = write!(&mut buf, "F{}::", tree.get_ident_str(*name).unwrap());
_ = write!(
&mut buf,
"F{}::",
tree.get_ident_str(*name).unwrap()
);
}
_ => {}
}
@ -69,11 +81,11 @@ impl InnerSymbolTable {
Self::new_with(Self::new_inner)
}
fn new_with<G>(r#gen: G) -> NonNull<InnerSymbolTable>
fn new_with<G>(gen: G) -> NonNull<InnerSymbolTable>
where
G: FnOnce() -> Self,
{
Box::into_non_null(Box::new(r#gen()))
NonNull::new(Box::leak(Box::new(gen())) as *mut _).unwrap()
}
fn new_inner() -> InnerSymbolTable {
@ -130,7 +142,12 @@ impl Drop for InnerSymbolTable {
}
impl InnerSymbolTable {
fn insert_symbol(&mut self, name: &str, node: AstNode, kind: SymbolKind) -> &SymbolRecord {
fn insert_symbol(
&mut self,
name: &str,
node: AstNode,
kind: SymbolKind,
) -> &SymbolRecord {
match kind {
SymbolKind::Var => {
self.ordered_identifiers.push(SymbolRecord {
@ -143,7 +160,11 @@ impl InnerSymbolTable {
}
}
fn insert_orderless_symbol(&mut self, name: &str, node: AstNode) -> &SymbolRecord {
fn insert_orderless_symbol(
&mut self,
name: &str,
node: AstNode,
) -> &SymbolRecord {
self.orderless_identifiers.insert(
name.to_owned(),
SymbolRecord {
@ -154,7 +175,11 @@ impl InnerSymbolTable {
self.orderless_identifiers.get(name).unwrap()
}
fn find_symbol_or_insert_with<'a, F>(&'a mut self, name: &str, cb: F) -> &'a SymbolRecord
fn find_symbol_or_insert_with<'a, F>(
&'a mut self,
name: &str,
cb: F,
) -> &'a SymbolRecord
where
F: FnOnce() -> (AstNode, SymbolKind),
{
@ -177,7 +202,9 @@ impl InnerSymbolTable {
.find(|(_, v)| v.decl == decl)
.map(|(_, v)| v)
})
.or_else(|| self.parent_ref().and_then(|p| p.find_symbol_by_decl(decl)))
.or_else(|| {
self.parent_ref().and_then(|p| p.find_symbol_by_decl(decl))
})
}
fn find_any_symbol(&self, name: &str) -> Option<&SymbolRecord> {
@ -192,7 +219,9 @@ impl InnerSymbolTable {
self.ordered_identifiers
.iter()
.find(|r| r.name.as_str() == name)
.or_else(|| self.parent_ref().and_then(|p| p.find_ordered_symbol(name)))
.or_else(|| {
self.parent_ref().and_then(|p| p.find_ordered_symbol(name))
})
}
fn find_orderless_symbol(&self, name: &str) -> Option<&SymbolRecord> {
@ -286,7 +315,12 @@ impl SymbolTableWrapper {
}
impl SymbolTableWrapper {
pub fn insert_symbol(&mut self, name: &str, node: AstNode, kind: SymbolKind) -> &SymbolRecord {
pub fn insert_symbol(
&mut self,
name: &str,
node: AstNode,
kind: SymbolKind,
) -> &SymbolRecord {
self.current_mut().insert_symbol(name, node, kind)
}
@ -294,15 +328,27 @@ impl SymbolTableWrapper {
self.root_mut().find_orderless_symbol(name)
}
pub fn insert_root_symbol(&mut self, name: &str, node: AstNode) -> &SymbolRecord {
pub fn insert_root_symbol(
&mut self,
name: &str,
node: AstNode,
) -> &SymbolRecord {
self.root_mut().insert_orderless_symbol(name, node)
}
pub fn insert_orderless_symbol(&mut self, name: &str, node: AstNode) -> &SymbolRecord {
pub fn insert_orderless_symbol(
&mut self,
name: &str,
node: AstNode,
) -> &SymbolRecord {
self.current_mut().insert_orderless_symbol(name, node)
}
pub fn find_symbol_or_insert_with<'a, F>(&'a mut self, name: &str, cb: F) -> &'a SymbolRecord
pub fn find_symbol_or_insert_with<'a, F>(
&'a mut self,
name: &str,
cb: F,
) -> &'a SymbolRecord
where
F: FnOnce() -> (AstNode, SymbolKind),
{
@ -404,8 +450,8 @@ pub mod syms2 {
use std::collections::BTreeMap;
use std::fmt::Debug;
use crate::ast2::Index as AstIndex;
use crate::ast2::intern::Index as InternIndex;
use crate::ast2::Index as AstIndex;
use crate::lexer::SourceLocation;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
@ -424,40 +470,6 @@ pub mod syms2 {
},
}
impl Key {
pub fn kind(&self) -> Option<SymbolKind> {
match self {
Key::Symbol { kind, .. } => Some(*kind),
_ => None,
}
}
}
#[repr(u32)]
pub enum DeclKind {
Local = 1,
Parameter,
}
impl DeclKind {
pub fn from_u32(v: u32) -> Option<Self> {
match v {
1 => Some(Self::Local),
2 => Some(Self::Parameter),
_ => None,
}
}
}
impl From<SymbolKind> for Option<DeclKind> {
fn from(value: SymbolKind) -> Self {
match value {
SymbolKind::Parameter(_) => Some(DeclKind::Parameter),
SymbolKind::Local(_) => Some(DeclKind::Local),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum SymbolKind {
__First,
@ -467,7 +479,6 @@ pub mod syms2 {
__TypeScope,
Scope,
ParentScope,
Parameter(SourceLocation),
Local(SourceLocation),
__Last,
}
@ -515,7 +526,9 @@ pub mod syms2 {
}
let entries = self.inner.iter().map(|(key, val)| {
let payload = match key {
Key::ScopeByIndex { .. } => ExpandedPayload::Intern(val.as_intern()),
Key::ScopeByIndex { .. } => {
ExpandedPayload::Intern(val.as_intern())
}
_ => ExpandedPayload::Ast(val.as_ast()),
};
@ -554,7 +567,7 @@ pub mod syms2 {
scope: AstIndex,
name: InternIndex,
loc: SourceLocation,
) -> Option<(Key, AstIndex)> {
) -> Option<AstIndex> {
use SymbolKind::*;
let range = self.inner.range(
Key::Symbol {
@ -568,8 +581,8 @@ pub mod syms2 {
},
);
if let Some((key, payload)) = range.rev().next() {
Some((*key, payload.as_ast()))
if let Some((_, payload)) = range.rev().next() {
Some(payload.as_ast())
} else {
if let Some(parent) = self.inner.get(&Key::Symbol {
scope,
@ -588,7 +601,7 @@ pub mod syms2 {
scope: AstIndex,
name: InternIndex,
loc: SourceLocation,
) -> Option<(Key, AstIndex)> {
) -> Option<AstIndex> {
use SymbolKind::*;
let range = self.inner.range(
Key::Symbol {
@ -602,15 +615,15 @@ pub mod syms2 {
},
);
if let Some((key, payload)) = range.rev().next() {
Some((*key, payload.as_ast()))
if let Some((_, payload)) = range.rev().next() {
Some(payload.as_ast())
} else {
if let Some(parent) = self.inner.get(&Key::Symbol {
scope,
name: InternIndex::invalid(),
kind: ParentScope,
}) {
self.find_type_symbol(parent.as_ast(), name, loc)
self.find_symbol(parent.as_ast(), name, loc)
} else {
None
}
@ -624,8 +637,10 @@ pub mod syms2 {
kind: SymbolKind,
ast: AstIndex,
) {
self.inner
.insert(Key::Symbol { scope, name, kind }, Payload::new_ast(ast));
self.inner.insert(
Key::Symbol { scope, name, kind },
Payload::new_ast(ast),
);
}
}
}

View file

@ -7,7 +7,7 @@ use std::{
use crate::{
ast::{Node as AstNode, Tag, Type},
ast2::intern::{self, AMD64_POINTER_BITS, AMD64_POINTER_TYPE_INFO, InternPool},
ast2::intern::{self, InternPool, AMD64_POINTER_BITS, AMD64_POINTER_TYPE_INFO},
parser::Tree,
variant, write_indented, writeln_indented,
};
@ -159,13 +159,10 @@ pub enum Inst {
Constant,
/// size, align
Alloca,
/// (pointee type)
/// src
Load(intern::Index),
/// (pointee type)
/// src, dst
Store(intern::Index),
/// (pointer type)
/// ptr, index,
GetElementPtr(intern::Index),
/// size, align
@ -608,10 +605,10 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> {
let ty = self
.tree
.peer_type_of_nodes(*lhs, *rhs)
.expect(&{
.expect({
let at = self.tree.type_of_node(*lhs);
let bt = self.tree.type_of_node(*rhs);
format!("incompatible types for %{lhs}({at}) and %{rhs}({bt})")
&format!("incompatible types for %{lhs}({at}) and %{rhs}({bt})")
})
.into();
@ -762,8 +759,8 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> {
}
pub struct IR {
pub(crate) nodes: Vec<Inst>,
pub(crate) data: Vec<Option<Data>>,
nodes: Vec<Inst>,
data: Vec<Option<Data>>,
// intern_pool: &'a mut InternPool,
}

View file

@ -1,25 +0,0 @@
use core::{cell::UnsafeCell, mem::ManuallyDrop};
pub fn unit<T>(_: T) {}
pub struct DropGuard<F: FnOnce()>(UnsafeCell<ManuallyDrop<F>>);
impl<F> DropGuard<F>
where
F: FnOnce(),
{
pub fn new(f: F) -> DropGuard<F> {
Self(UnsafeCell::new(ManuallyDrop::new(f)))
}
}
impl<F> Drop for DropGuard<F>
where
F: FnOnce(),
{
fn drop(&mut self) {
unsafe {
ManuallyDrop::take(&mut *self.0.get())();
}
}
}