Compare commits

...

42 commits

Author SHA1 Message Date
janis 45cc444221
constants parsing 2025-10-01 17:56:02 +02:00
janis c270fe5add
comments and docs as ast nodes 2025-10-01 00:31:08 +02:00
janis 45ba06db43
use pomelo for parsing 2025-10-01 00:24:46 +02:00
janis 816aebda01
try out pomelo parser 2025-09-30 17:29:38 +02:00
janis 357590ec07
more useless token stuff that I didn't really want anyway 2025-09-30 16:44:42 +02:00
janis df2bb54272
parser crate 2025-09-29 15:56:13 +02:00
janis ae0fb53b90
stuff.. 2025-09-29 15:56:05 +02:00
janis 260150de15
more iterator stuff 2025-09-27 22:16:04 +02:00
janis 2790bc561f
more refactoring 2025-09-27 18:20:52 +02:00
janis 6e0fed0962
raw identifiers, comment out -- and ++ tokens 2025-09-27 17:07:50 +02:00
janis 122f8ff7f1
compiler wranings 2025-09-26 14:58:39 +02:00
janis 2e6b8b0cc3
add integer types 2025-09-26 14:52:53 +02:00
janis 2293b514cf
idk man.. 2025-09-19 20:12:21 +02:00
janis 82d2eed09a
lexer crate 2025-09-19 20:12:13 +02:00
janis fafd4011e2
token iterator based on trie 2025-09-19 14:46:48 +02:00
janis 47e671f827
update flake 2025-09-19 14:46:42 +02:00
janis 21939cabe3
fix workspace dependency on trie 2025-09-19 14:46:34 +02:00
janis f65aa8d921
-- and ++ tokens 2025-09-19 13:47:49 +02:00
janis 735b60f3cb
add trie dependency 2025-09-19 13:47:32 +02:00
Janis 0384a98d86 chore(deps): update flake.lock to rustc 1.91.0-nightly 25-08-06 2025-08-08 00:34:47 +02:00
Janis 3bbf9d67ec tree lexer 2025-08-07 03:21:27 +02:00
Janis bc0acf7e19 lexer crate 2025-08-07 02:00:44 +02:00
Janis 0fa3200b85 ignore .direnv folder 2025-08-06 22:27:09 +02:00
Janis 76e50085a0 idk man, intern stuff innit 2025-08-06 22:27:00 +02:00
Janis d124ae2b59 nix flake/direnv 2025-08-02 21:25:04 +02:00
Janis adfc2c27f8 top and bottom types 2025-07-14 18:10:52 +02:00
Janis bbaa9cb671 update to edition 2024 and 1.90-nightly 2025-07-14 17:52:03 +02:00
Janis aeab786fe3 todo: typechecking, then ensure placeness and IR still work as expected 2025-03-08 12:22:53 +01:00
Janis 4c7813aa98 aaaaaaaaaaaaaaaa 2025-03-06 23:46:45 +01:00
Janis 1bde8f3ccd new trait for ast for getting nice nodes 2025-03-06 22:36:53 +01:00
Janis 486a634ea1 more fixes 2025-03-06 18:40:17 +01:00
Janis 8b3789d7b0 placeness solver on ast after parsing 2025-03-06 15:41:34 +01:00
Janis d6450b5f32 using the new visitor for debug rendering 2025-03-06 12:41:39 +01:00
Janis f30b7827b8 pulled out modules into separate files 2025-03-06 02:28:02 +01:00
Janis 6cc1822ec6 trying to fix declrefs to params being places 2025-03-06 02:18:46 +01:00
Janis 8c95a2ba3d single-threaded interior mutably wrapper for internpool 2025-03-06 02:18:15 +01:00
Janis f5fc8195f4 fix, lint 2025-03-06 01:12:25 +01:00
Janis 1be3f29e23 place-value stuff should work now 2025-03-06 01:02:35 +01:00
Janis 6d70231c91 this should be correct? 2025-03-05 22:21:36 +01:00
Janis 51aa119af2 i dont think this is the right way to do this, in hindsight 2025-03-05 15:49:19 +01:00
Janis 028c74753e place and value expression conversion in ast 2025-03-05 15:48:46 +01:00
Janis 1a20acd763 ast visiting for IR building 2025-03-04 18:58:55 +01:00
29 changed files with 8294 additions and 2924 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake

1
.gitignore vendored
View file

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

View file

@ -1,7 +1,13 @@
[workspace]
resolver = "3"
members = [
"crates/lexer"
, "crates/parser"]
[package] [package]
name = "compiler" name = "compiler"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2024"
[dependencies] [dependencies]
ansi_term = "0.12.1" ansi_term = "0.12.1"
@ -12,6 +18,20 @@ log = "0.4.22"
num-bigint = "0.4.6" num-bigint = "0.4.6"
num-traits = "0.2.19" num-traits = "0.2.19"
ordered-float = "4.2.2" ordered-float = "4.2.2"
paste = "1.0.15"
petgraph = "0.6.5" petgraph = "0.6.5"
thiserror = "1.0.63" thiserror = "1.0.63"
unicode-xid = "0.2.4" 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" }

14
crates/lexer/Cargo.toml Normal file
View file

@ -0,0 +1,14 @@
[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

@ -0,0 +1,473 @@
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)
);
}
}

818
crates/lexer/src/lib.rs Normal file
View file

@ -0,0 +1,818 @@
#![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);
}
}

17
crates/parser/Cargo.toml Normal file
View file

@ -0,0 +1,17 @@
[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

@ -0,0 +1,104 @@
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)
}

665
crates/parser/src/lib.rs Normal file
View file

@ -0,0 +1,665 @@
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);
}
}

96
flake.lock Normal file
View file

@ -0,0 +1,96 @@
{
"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
}

31
flake.nix Normal file
View file

@ -0,0 +1,31 @@
{
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
];
};
});
}

204
src/ast2/biunification.rs Normal file
View file

@ -0,0 +1,204 @@
//! 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!()
}
}

159
src/ast2/debug.rs Normal file
View file

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

34
src/ast2/internable.rs Normal file
View file

@ -0,0 +1,34 @@
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()
}
}

1233
src/ast2/ir.rs Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1906
src/ast2/parser.rs Normal file

File diff suppressed because it is too large Load diff

631
src/ast2/tag.rs Normal file
View file

@ -0,0 +1,631 @@
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,
}

502
src/ast2/typecheck.rs Normal file
View file

@ -0,0 +1,502 @@
// 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)
}
}

625
src/ast2/visitor.rs Normal file
View file

@ -0,0 +1,625 @@
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}"); println!("AST:\n{buf}");
} }
"ast2" => { "ast2" => {
let mut tree2 = compiler::ast2::ast_gen::Parser::new(); let mut tree2 = compiler::ast2::parser::Parser::new();
tree2.parse(tokens.iter()); tree2.parse(tokens.iter());
eprintln!("{tree2:#?}"); eprintln!("{tree2:#?}");
println!("AST (new):\n{tree2}"); println!("AST (new):\n{tree2}");

View file

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

View file

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

View file

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

View file

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

View file

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

25
src/utils.rs Normal file
View file

@ -0,0 +1,25 @@
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())();
}
}
}