Compare commits
56 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ac27f73dd | ||
|
|
51ef019fd1 | ||
|
|
0ee6bbad61 | ||
|
|
0468f1fab3 | ||
|
|
a2632ca06e | ||
|
|
c7d5e4e6c0 | ||
|
|
2771593605 | ||
|
|
b0b87c68f2 | ||
|
|
f67bb61888 | ||
|
|
77bd4f3f16 | ||
|
|
9a799ea281 | ||
|
|
5aba59b291 | ||
|
|
882f30371e | ||
|
|
0efd60c3e6 | ||
|
|
45cc444221 | ||
|
|
c270fe5add | ||
|
|
45ba06db43 | ||
|
|
816aebda01 | ||
|
|
357590ec07 | ||
|
|
df2bb54272 | ||
|
|
ae0fb53b90 | ||
|
|
260150de15 | ||
|
|
2790bc561f | ||
|
|
6e0fed0962 | ||
|
|
122f8ff7f1 | ||
|
|
2e6b8b0cc3 | ||
|
|
2293b514cf | ||
|
|
82d2eed09a | ||
|
|
fafd4011e2 | ||
|
|
47e671f827 | ||
|
|
21939cabe3 | ||
|
|
f65aa8d921 | ||
|
|
735b60f3cb | ||
|
|
0384a98d86 | ||
|
|
3bbf9d67ec | ||
|
|
bc0acf7e19 | ||
|
|
0fa3200b85 | ||
|
|
76e50085a0 | ||
|
|
d124ae2b59 | ||
|
|
adfc2c27f8 | ||
|
|
bbaa9cb671 | ||
|
|
aeab786fe3 | ||
|
|
4c7813aa98 | ||
|
|
1bde8f3ccd | ||
|
|
486a634ea1 | ||
|
|
8b3789d7b0 | ||
|
|
d6450b5f32 | ||
|
|
f30b7827b8 | ||
|
|
6cc1822ec6 | ||
|
|
8c95a2ba3d | ||
|
|
f5fc8195f4 | ||
|
|
1be3f29e23 | ||
|
|
6d70231c91 | ||
|
|
51aa119af2 | ||
|
|
028c74753e | ||
|
|
1a20acd763 |
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
|||
/target
|
||||
/Cargo.lock
|
||||
/.direnv/
|
||||
|
|
|
|||
22
Cargo.toml
22
Cargo.toml
|
|
@ -1,7 +1,13 @@
|
|||
[workspace]
|
||||
resolver = "3"
|
||||
members = [
|
||||
"crates/lexer"
|
||||
, "crates/parser"]
|
||||
|
||||
[package]
|
||||
name = "compiler"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
ansi_term = "0.12.1"
|
||||
|
|
@ -12,6 +18,20 @@ log = "0.4.22"
|
|||
num-bigint = "0.4.6"
|
||||
num-traits = "0.2.19"
|
||||
ordered-float = "4.2.2"
|
||||
paste = "1.0.15"
|
||||
petgraph = "0.6.5"
|
||||
thiserror = "1.0.63"
|
||||
unicode-xid = "0.2.4"
|
||||
tracing = "0.1.41"
|
||||
|
||||
werkzeug = { path = "../../rust/werkzeug" }
|
||||
|
||||
|
||||
[workspace.dependencies]
|
||||
unicode-xid = "0.2.4"
|
||||
tracing = "0.1.41"
|
||||
thiserror = "1.0.63"
|
||||
itertools = "0.13.0"
|
||||
|
||||
werkzeug = { path = "../../rust/werkzeug" }
|
||||
trie = { path = "../../rust/trie" }
|
||||
|
|
|
|||
14
crates/lexer/Cargo.toml
Normal file
14
crates/lexer/Cargo.toml
Normal 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"
|
||||
460
crates/lexer/src/complex_tokens.rs
Normal file
460
crates/lexer/src/complex_tokens.rs
Normal file
|
|
@ -0,0 +1,460 @@
|
|||
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,
|
||||
}
|
||||
}
|
||||
|
||||
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!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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 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();
|
||||
loop {
|
||||
// take until new line
|
||||
source.take_while_inclusive(|&c| c != '\n').for_each(drop);
|
||||
|
||||
let mut copy = source.clone();
|
||||
// skip whitespaces after new line to find continuation of comment
|
||||
(&mut copy)
|
||||
.take_while_ref(|&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 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)
|
||||
);
|
||||
}
|
||||
}
|
||||
822
crates/lexer/src/lib.rs
Normal file
822
crates/lexer/src/lib.rs
Normal file
|
|
@ -0,0 +1,822 @@
|
|||
#![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 == '_' || 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'
|
||||
}
|
||||
|
||||
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, sync::Arc};
|
||||
|
||||
use trie::Tree;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TokenItem<'a> {
|
||||
pub token: Token<'a>,
|
||||
pub span: Range<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>>>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TokenIterator<'a> {
|
||||
trie: Arc<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);
|
||||
}
|
||||
|
||||
let trie = Arc::new(trie);
|
||||
|
||||
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();
|
||||
|
||||
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,
|
||||
span: range.start as u32..range.end 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)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
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>;
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
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);
|
||||
}
|
||||
}
|
||||
16
crates/parser/Cargo.toml
Normal file
16
crates/parser/Cargo.toml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
[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" }
|
||||
|
||||
chumsky = {version = "0.11", features = ["pratt"] }
|
||||
104
crates/parser/src/constants.rs
Normal file
104
crates/parser/src/constants.rs
Normal 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)
|
||||
}
|
||||
1300
crates/parser/src/lib.rs
Normal file
1300
crates/parser/src/lib.rs
Normal file
File diff suppressed because it is too large
Load diff
398
crates/parser/src/pretty.rs
Normal file
398
crates/parser/src/pretty.rs
Normal file
|
|
@ -0,0 +1,398 @@
|
|||
use crate::{Ast, AstNode, Index};
|
||||
|
||||
pub struct PrettyPrint {
|
||||
lines: Vec<String>,
|
||||
indents: Vec<Indent>,
|
||||
}
|
||||
|
||||
const VERTICAL: &str = "│";
|
||||
const HORIZONTAL: &str = "─";
|
||||
const CONNECTOR: &str = "├";
|
||||
const END_CONNECTOR: &str = "└";
|
||||
const EMPTY: &str = " ";
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
enum Indent {
|
||||
Vertical,
|
||||
End,
|
||||
Empty,
|
||||
}
|
||||
|
||||
impl PrettyPrint {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
lines: Vec::new(),
|
||||
indents: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn push_line(&mut self, line: String) {
|
||||
let (last, rest) = self
|
||||
.indents
|
||||
.split_last_mut()
|
||||
.map_or::<(Option<&mut Indent>, &mut [Indent]), _>((None, &mut []), |(last, rest)| {
|
||||
(Some(last), rest)
|
||||
});
|
||||
let rest = rest.iter_mut().map(|indent| match *indent {
|
||||
Indent::Vertical => VERTICAL,
|
||||
Indent::End => {
|
||||
*indent = Indent::Empty;
|
||||
END_CONNECTOR
|
||||
}
|
||||
Indent::Empty => EMPTY,
|
||||
});
|
||||
let last = if let Some(last) = last {
|
||||
match last {
|
||||
Indent::Vertical => CONNECTOR,
|
||||
Indent::End => {
|
||||
*last = Indent::Empty;
|
||||
END_CONNECTOR
|
||||
}
|
||||
Indent::Empty => CONNECTOR,
|
||||
}
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
self.lines.push(
|
||||
rest.chain(std::iter::once(last))
|
||||
.chain(std::iter::once(line.as_str()))
|
||||
.collect::<String>(),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn print(mut self, ast: &Ast) {
|
||||
let root = ast.nodes.len().checked_sub(1).unwrap();
|
||||
self.stuff(ast, Index(root as u32));
|
||||
for line in self.lines {
|
||||
println!("{}", line);
|
||||
}
|
||||
}
|
||||
|
||||
fn with_indent<I>(
|
||||
&mut self,
|
||||
mut items: impl DoubleEndedIterator<Item = I>,
|
||||
mut f: impl FnMut(&mut Self, I),
|
||||
) {
|
||||
if let Some(last) = (&mut items).next_back() {
|
||||
self.indents.push(Indent::Vertical);
|
||||
for item in items {
|
||||
f(self, item);
|
||||
}
|
||||
*self.indents.last_mut().unwrap() = Indent::End;
|
||||
f(self, last);
|
||||
self.indents.pop();
|
||||
}
|
||||
}
|
||||
|
||||
fn stuff(&mut self, ast: &Ast, node: Index) {
|
||||
let mut node = ast.nodes.get(node.0 as usize).unwrap();
|
||||
|
||||
match node {
|
||||
AstNode::Root { files } => {
|
||||
self.push_line(format!(
|
||||
"{} {{num_files: {}}}",
|
||||
node_name(node),
|
||||
files.len()
|
||||
));
|
||||
|
||||
self.with_indent(files.iter(), |this, idx| {
|
||||
this.stuff(ast, *idx);
|
||||
});
|
||||
}
|
||||
AstNode::File { decls } => {
|
||||
self.push_line(format!(
|
||||
"{} {{num_decls: {}}}",
|
||||
node_name(node),
|
||||
decls.len()
|
||||
));
|
||||
|
||||
self.with_indent(decls.iter(), |this, idx| {
|
||||
this.stuff(ast, *idx);
|
||||
});
|
||||
}
|
||||
AstNode::ParameterList(parameters) => {
|
||||
self.push_line(format!(
|
||||
"{}[{}]",
|
||||
node_name(node),
|
||||
parameters.parameters.len()
|
||||
));
|
||||
|
||||
self.with_indent(parameters.parameters.iter(), |this, idx| {
|
||||
this.stuff(ast, *idx);
|
||||
});
|
||||
}
|
||||
AstNode::Parameter(parameter) => {
|
||||
self.push_line(format!(
|
||||
"{} [{}: {}]",
|
||||
node_name(node),
|
||||
parameter.name,
|
||||
parameter.param_type
|
||||
));
|
||||
}
|
||||
AstNode::FunctionDecl(function_decl) => {
|
||||
self.push_line(format!("{} {}", node_name(node), function_decl.name,));
|
||||
self.indents.push(Indent::Vertical);
|
||||
self.push_line(format!("VISIBILITY: {:?}", function_decl.visibility));
|
||||
self.stuff(ast, function_decl.parameter_list);
|
||||
self.push_line(format!("RETURN_TYPE: {}", function_decl.return_type));
|
||||
*self.indents.last_mut().unwrap() = Indent::End;
|
||||
self.stuff(ast, function_decl.body);
|
||||
self.indents.pop();
|
||||
}
|
||||
AstNode::Block { statements, expr } => {
|
||||
self.push_line(format!("{}", node_name(node),));
|
||||
|
||||
self.with_indent(statements.iter().chain(expr), |this, idx| {
|
||||
this.stuff(ast, *idx);
|
||||
});
|
||||
}
|
||||
AstNode::Constant { ty, value } => {
|
||||
self.push_line(format!("{} [{} := {:?}]", node_name(node), ty, value));
|
||||
}
|
||||
AstNode::NoopExpr => {
|
||||
self.push_line(format!("{}", node_name(node),));
|
||||
}
|
||||
AstNode::Stmt { expr } => {
|
||||
self.push_line(format!("{}", node_name(node),));
|
||||
self.indents.push(Indent::End);
|
||||
self.stuff(ast, *expr);
|
||||
self.indents.pop();
|
||||
}
|
||||
AstNode::ControlFlow { kind, expr } => todo!(),
|
||||
AstNode::VarDecl {
|
||||
mutable,
|
||||
name,
|
||||
var_type,
|
||||
} => {
|
||||
self.push_line(format!(
|
||||
"{} [{} {}: {}]",
|
||||
node_name(node),
|
||||
name,
|
||||
mutable.then_some(" mut").unwrap_or(""),
|
||||
var_type.map_or("?".to_string(), |ty| format!("{ty}"))
|
||||
));
|
||||
}
|
||||
AstNode::Assignment { dest, expr } => {
|
||||
self.push_line(format!("{}", node_name(node),));
|
||||
self.indents.push(Indent::Vertical);
|
||||
self.push_line("DEST".to_string());
|
||||
self.with_indent(core::iter::once(*dest), |this, idx| {
|
||||
this.stuff(ast, idx);
|
||||
});
|
||||
*self.indents.last_mut().unwrap() = Indent::End;
|
||||
self.push_line("EXPR".to_string());
|
||||
self.with_indent(core::iter::once(*expr), |this, idx| {
|
||||
this.stuff(ast, idx);
|
||||
});
|
||||
self.indents.pop();
|
||||
}
|
||||
AstNode::GlobalDecl {
|
||||
name,
|
||||
var_type,
|
||||
expr,
|
||||
} => {
|
||||
self.push_line(format!("{} [{}: {}]", node_name(node), name, var_type));
|
||||
self.with_indent(core::iter::once(*expr), |this, idx| {
|
||||
this.stuff(ast, idx);
|
||||
});
|
||||
}
|
||||
AstNode::StructDecl { name, fields } => todo!(),
|
||||
AstNode::FieldDecl { name, field_type } => todo!(),
|
||||
AstNode::FieldAccess { expr, field } => todo!(),
|
||||
AstNode::UnresolvedDeclRef { name } => {
|
||||
self.push_line(format!("{} \"{}\"", node_name(node), name,));
|
||||
}
|
||||
AstNode::DeclRef { decl } => {
|
||||
self.push_line(format!("{} @{}", node_name(node), decl.0,));
|
||||
}
|
||||
AstNode::TypeDeclRef { ty } => {
|
||||
self.push_line(format!("{} @{:?}", node_name(node), ty,));
|
||||
}
|
||||
AstNode::ExplicitCast { expr, ty } => {
|
||||
self.push_line(format!("{} {}", node_name(node), ty,));
|
||||
self.indents.push(Indent::End);
|
||||
self.stuff(ast, *expr);
|
||||
self.indents.pop();
|
||||
}
|
||||
AstNode::Not(expr)
|
||||
| AstNode::Negate(expr)
|
||||
| AstNode::Deref { expr }
|
||||
| AstNode::AddressOf { expr }
|
||||
| AstNode::ValueToPlace { expr }
|
||||
| AstNode::PlaceToValue { expr } => {
|
||||
self.push_line(format!("{}", node_name(node),));
|
||||
self.indents.push(Indent::End);
|
||||
self.stuff(ast, *expr);
|
||||
self.indents.pop();
|
||||
}
|
||||
AstNode::CallExpr { callee, arguments } => {
|
||||
self.push_line(format!(
|
||||
"{} {{num_args: {}}}",
|
||||
node_name(node),
|
||||
arguments.len()
|
||||
));
|
||||
self.indents.push(Indent::Vertical);
|
||||
self.stuff(ast, *callee);
|
||||
*self.indents.last_mut().unwrap() = Indent::End;
|
||||
self.with_indent(arguments.iter(), |this, arg| {
|
||||
this.stuff(ast, *arg);
|
||||
});
|
||||
self.indents.pop();
|
||||
}
|
||||
AstNode::Argument { expr } => {
|
||||
self.stuff(ast, *expr);
|
||||
}
|
||||
AstNode::Multiply { left, right }
|
||||
| AstNode::Divide { left, right }
|
||||
| AstNode::Modulus { left, right }
|
||||
| AstNode::Add { left, right }
|
||||
| AstNode::Subtract { left, right }
|
||||
| AstNode::BitOr { left, right }
|
||||
| AstNode::BitAnd { left, right }
|
||||
| AstNode::BitXor { left, right }
|
||||
| AstNode::LogicalOr { left, right }
|
||||
| AstNode::LogicalAnd { left, right }
|
||||
| AstNode::Eq { left, right }
|
||||
| AstNode::NotEq { left, right }
|
||||
| AstNode::Less { left, right }
|
||||
| AstNode::LessEq { left, right }
|
||||
| AstNode::Greater { left, right }
|
||||
| AstNode::GreaterEq { left, right }
|
||||
| AstNode::ShiftLeft { left, right }
|
||||
| AstNode::ShiftRight { left, right } => {
|
||||
self.push_line(format!("{}", node_name(node),));
|
||||
self.with_indent([*left, *right].into_iter(), |this, idx| {
|
||||
this.stuff(ast, idx);
|
||||
});
|
||||
}
|
||||
AstNode::Subscript { expr, index } => {
|
||||
self.push_line(format!("{}", node_name(node),));
|
||||
self.indents.push(Indent::Vertical);
|
||||
self.stuff(ast, *expr);
|
||||
*self.indents.last_mut().unwrap() = Indent::End;
|
||||
|
||||
self.push_line("INDEX".to_string());
|
||||
self.with_indent([*index].into_iter(), |this, idx| {
|
||||
this.stuff(ast, idx);
|
||||
});
|
||||
|
||||
self.indents.pop();
|
||||
}
|
||||
AstNode::If {
|
||||
condition,
|
||||
then,
|
||||
r#else,
|
||||
} => {
|
||||
self.push_line(format!("{}", node_name(node),));
|
||||
self.indents.push(Indent::Vertical);
|
||||
self.push_line("COND".to_string());
|
||||
self.with_indent([*condition].into_iter(), |this, idx| {
|
||||
this.stuff(ast, idx);
|
||||
});
|
||||
|
||||
self.push_line("THEN".to_string());
|
||||
if let Some(r#else) = r#else {
|
||||
self.with_indent(core::iter::once(*then), |this, idx| {
|
||||
this.stuff(ast, idx);
|
||||
});
|
||||
*self.indents.last_mut().unwrap() = Indent::End;
|
||||
self.push_line("ELSE".to_string());
|
||||
self.with_indent(core::iter::once(*r#else), |this, idx| {
|
||||
this.stuff(ast, idx);
|
||||
});
|
||||
} else {
|
||||
*self.indents.last_mut().unwrap() = Indent::End;
|
||||
self.with_indent(core::iter::once(*then), |this, idx| {
|
||||
this.stuff(ast, idx);
|
||||
});
|
||||
}
|
||||
self.indents.pop();
|
||||
}
|
||||
AstNode::Else { expr } => {
|
||||
self.push_line(format!("{}", node_name(node),));
|
||||
self.indents.push(Indent::End);
|
||||
self.stuff(ast, *expr);
|
||||
self.indents.pop();
|
||||
}
|
||||
AstNode::Comment { text } => {
|
||||
self.push_line(format!("{} \"{}\"", node_name(node), text,));
|
||||
}
|
||||
AstNode::Attributes { attrs } => {
|
||||
self.push_line(format!(
|
||||
"{} {{num_attrs: {}}}",
|
||||
node_name(node),
|
||||
attrs.len()
|
||||
));
|
||||
self.with_indent(attrs.iter(), |this, &attr| {
|
||||
this.stuff(ast, attr);
|
||||
});
|
||||
}
|
||||
AstNode::Doc { text } => {
|
||||
self.push_line(format!("{} \"{}\"", node_name(node), text,));
|
||||
}
|
||||
|
||||
AstNode::Error { err } => {
|
||||
self.push_line(format!("{} \"{}\"", node_name(node), err,));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn node_name(node: &AstNode) -> &'static str {
|
||||
match node {
|
||||
AstNode::Root { .. } => "ROOT",
|
||||
AstNode::File { .. } => "FILE",
|
||||
AstNode::ParameterList(_) => "PARAMS",
|
||||
AstNode::Parameter(_) => "PARAM",
|
||||
AstNode::FunctionDecl(_) => "FN",
|
||||
AstNode::Block { .. } => "BLOCK",
|
||||
AstNode::Constant { .. } => "CONSTANT",
|
||||
AstNode::NoopExpr => "NOOP",
|
||||
AstNode::Stmt { .. } => "STMT",
|
||||
AstNode::ControlFlow { .. } => "CONTROL_FLOW",
|
||||
AstNode::VarDecl { .. } => "VAR_DECL",
|
||||
AstNode::Assignment { .. } => "ASSIGN",
|
||||
AstNode::GlobalDecl { .. } => "GLOBAL_DECL",
|
||||
AstNode::StructDecl { .. } => "STRUCT",
|
||||
AstNode::FieldDecl { .. } => "FIELD",
|
||||
AstNode::FieldAccess { .. } => "FIELD_ACCESS",
|
||||
AstNode::UnresolvedDeclRef { .. } => "UNRESOLVED_DECL_REF",
|
||||
AstNode::DeclRef { .. } => "DECL_REF",
|
||||
AstNode::TypeDeclRef { .. } => "TYPE_REF",
|
||||
AstNode::ExplicitCast { .. } => "AS",
|
||||
AstNode::Deref { .. } => "DEREF",
|
||||
AstNode::AddressOf { .. } => "ADDR_OF",
|
||||
AstNode::PlaceToValue { .. } => "INTO_VALUE",
|
||||
AstNode::ValueToPlace { .. } => "INTO_PLACE",
|
||||
AstNode::CallExpr { .. } => "CALL",
|
||||
AstNode::Argument { .. } => "ARG",
|
||||
AstNode::Not(_) => "NOT",
|
||||
AstNode::Negate(_) => "NEGATE",
|
||||
AstNode::Multiply { .. } => "MUL",
|
||||
AstNode::Divide { .. } => "DIV",
|
||||
AstNode::Modulus { .. } => "REM",
|
||||
AstNode::Add { .. } => "ADD",
|
||||
AstNode::Subtract { .. } => "SUB",
|
||||
AstNode::BitOr { .. } => "BIT_OR",
|
||||
AstNode::BitAnd { .. } => "BIT_AND",
|
||||
AstNode::BitXor { .. } => "BIT_XOR",
|
||||
AstNode::LogicalOr { .. } => "OR",
|
||||
AstNode::LogicalAnd { .. } => "AND",
|
||||
AstNode::Eq { .. } => "EQ",
|
||||
AstNode::NotEq { .. } => "NEQ",
|
||||
AstNode::Less { .. } => "LT",
|
||||
AstNode::LessEq { .. } => "LEQ",
|
||||
AstNode::Greater { .. } => "GT",
|
||||
AstNode::GreaterEq { .. } => "GEQ",
|
||||
AstNode::ShiftLeft { .. } => "SHL",
|
||||
AstNode::ShiftRight { .. } => "SHR",
|
||||
AstNode::Subscript { .. } => "SUBSCRIPT",
|
||||
AstNode::If { .. } => "IF",
|
||||
AstNode::Else { .. } => "ELSE",
|
||||
AstNode::Comment { .. } => "COMMENT",
|
||||
AstNode::Attributes { .. } => "META",
|
||||
AstNode::Doc { .. } => "DOCS",
|
||||
AstNode::Error { .. } => "ERR",
|
||||
}
|
||||
}
|
||||
284
crates/parser/src/symbols.rs
Normal file
284
crates/parser/src/symbols.rs
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
//! Coming from the ast, we have a `DeclRef` with an interned identifier `ident`
|
||||
//! and want to find the symbol it refers to.
|
||||
//!
|
||||
//! To help, we have a struct keeping track of all accessible scopes. Now, we
|
||||
//! want to look through any accessible scope `s` for a symbol with the name
|
||||
//! `ident`. Thus: `Symbol {scope: `s`, name: `ident`, ..}`.
|
||||
//!
|
||||
//! We might also know the type of the symbol we are looking for, if we want
|
||||
//! to permit fields/variables and methods/functions sharing names.
|
||||
//!
|
||||
//! Since I want to allow variable shadowing for local variables, some strategy
|
||||
//! to differentiate between shadowed variables must be employed:
|
||||
//! - keys of type SymbolKind::Local might point to a list of values with source locations
|
||||
//! - keys might contain source locations.
|
||||
//!
|
||||
//! Any symbol pointed at from within the ast must again point at an ast
|
||||
//! object.
|
||||
//! Thus: `Key` -> `AstIndex`
|
||||
//! Exception: `Key::ScopeByIndex` -> `InternIndex`
|
||||
|
||||
use core::fmt::Debug;
|
||||
use std::{collections::BTreeMap, ops::Index as IndexOp};
|
||||
|
||||
use internment::Intern;
|
||||
|
||||
use crate::{Ast, Index};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Span(pub u32);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Spanned<T>(pub T, Span);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum SymbolKind {
|
||||
__First,
|
||||
Const,
|
||||
Function,
|
||||
Type,
|
||||
__TypeScope,
|
||||
Scope,
|
||||
ParentScope,
|
||||
Parameter(Span),
|
||||
Local(Span),
|
||||
__Last,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Key {
|
||||
ScopeByName {
|
||||
name: Intern<str>,
|
||||
},
|
||||
/// not all scopes have a name, as some are anonymous blocks or otherwise nameless
|
||||
ScopeByIndex {
|
||||
ast: Index,
|
||||
},
|
||||
Parent(Index),
|
||||
Symbol {
|
||||
scope: Index,
|
||||
sym: Symbol,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Symbol {
|
||||
pub name: Intern<str>,
|
||||
pub kind: SymbolKind,
|
||||
}
|
||||
|
||||
impl Key {
|
||||
pub fn parent(scope: Index) -> Key {
|
||||
Key::Parent(scope)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Payload {
|
||||
Ast(Index),
|
||||
Name(Intern<str>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Symbols {
|
||||
inner: BTreeMap<Key, Payload>,
|
||||
}
|
||||
|
||||
impl Debug for Symbols {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Symbols [")?;
|
||||
if f.alternate() {
|
||||
writeln!(f, "")?;
|
||||
}
|
||||
let entries = self.inner.iter();
|
||||
f.debug_list().entries(entries).finish()?;
|
||||
write!(f, "]")?;
|
||||
if f.alternate() {
|
||||
writeln!(f, "")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// checks for each scope in scopes_in_tree Symbol { scope, kind: SymbolKind::Local, 0}..Symbol { scope, kind: SymbolKind::Scope, u32::MAX}
|
||||
struct SymbolTreePos {
|
||||
scopes_in_scope: Vec<Index>,
|
||||
}
|
||||
|
||||
impl Symbols {
|
||||
pub fn new() -> Symbols {
|
||||
Self {
|
||||
inner: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
pub fn insert_scope(&mut self, ast: Index, name: Option<Intern<str>>, parent: Option<Index>) {
|
||||
if let Some(name) = name {
|
||||
self.inner
|
||||
.insert(Key::ScopeByIndex { ast }, Payload::Name(name));
|
||||
self.inner
|
||||
.insert(Key::ScopeByName { name }, Payload::Ast(ast));
|
||||
}
|
||||
if let Some(parent) = parent {
|
||||
self.inner.insert(Key::parent(ast), Payload::Ast(parent));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_symbol(&mut self, sym: Symbol, scope: Index, ast: Index) {
|
||||
self.inner
|
||||
.insert(Key::Symbol { scope, sym }, Payload::Ast(ast));
|
||||
}
|
||||
|
||||
pub fn find_symbol(&self, scope: Index, name: Intern<str>, loc: Span) -> Option<(Key, Index)> {
|
||||
use SymbolKind::*;
|
||||
let range = self.inner.range(
|
||||
Key::Symbol {
|
||||
scope,
|
||||
sym: Symbol {
|
||||
name,
|
||||
kind: __First,
|
||||
},
|
||||
}..=Key::Symbol {
|
||||
scope,
|
||||
sym: Symbol {
|
||||
name,
|
||||
kind: Local(loc),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if let Some((&key, &Payload::Ast(index))) = range.rev().next() {
|
||||
Some((key, index))
|
||||
} else {
|
||||
if let Some(&Payload::Ast(parent)) = self.inner.get(&Key::parent(scope)) {
|
||||
self.find_symbol(parent, name, loc)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_type_symbol(
|
||||
&self,
|
||||
scope: Index,
|
||||
name: Intern<str>,
|
||||
loc: Span,
|
||||
) -> Option<(Key, Index)> {
|
||||
use SymbolKind::*;
|
||||
let range = self.inner.range(
|
||||
Key::Symbol {
|
||||
scope,
|
||||
sym: Symbol {
|
||||
name,
|
||||
kind: __First,
|
||||
},
|
||||
}..=Key::Symbol {
|
||||
scope,
|
||||
sym: Symbol {
|
||||
name,
|
||||
kind: __TypeScope,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if let Some((&key, &Payload::Ast(index))) = range.rev().next() {
|
||||
Some((key, index))
|
||||
} else {
|
||||
if let Some(&Payload::Ast(parent)) = self.inner.get(&Key::parent(scope)) {
|
||||
self.find_type_symbol(parent, name, loc)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pre_symbol_pass(ast: &mut Ast) {
|
||||
struct Ctx<'a> {
|
||||
symbols: Symbols,
|
||||
scopes: Vec<Index>,
|
||||
ast: &'a mut Ast,
|
||||
}
|
||||
|
||||
let mut ctx = Ctx {
|
||||
ast,
|
||||
symbols: Symbols::new(),
|
||||
scopes: Vec::new(),
|
||||
};
|
||||
|
||||
fn process_node(ctx: &mut Ctx<'_>, index: Index) {
|
||||
match ctx.ast.index(index).clone() {
|
||||
crate::AstNode::Root { files } => {
|
||||
for file in files {
|
||||
process_node(ctx, file);
|
||||
}
|
||||
}
|
||||
crate::AstNode::File { decls } => todo!(),
|
||||
crate::AstNode::ParameterList(parameter_list) => todo!(),
|
||||
crate::AstNode::Parameter(parameter) => todo!(),
|
||||
crate::AstNode::FunctionDecl(function_decl) => todo!(),
|
||||
crate::AstNode::Block { statements, expr } => todo!(),
|
||||
crate::AstNode::Constant { ty, value } => todo!(),
|
||||
crate::AstNode::NoopExpr => todo!(),
|
||||
crate::AstNode::Stmt { expr } => todo!(),
|
||||
crate::AstNode::ControlFlow { kind, expr } => todo!(),
|
||||
crate::AstNode::VarDecl {
|
||||
mutable,
|
||||
name,
|
||||
var_type,
|
||||
} => todo!(),
|
||||
crate::AstNode::Assignment { dest, expr } => todo!(),
|
||||
crate::AstNode::GlobalDecl {
|
||||
name,
|
||||
var_type,
|
||||
expr,
|
||||
} => todo!(),
|
||||
crate::AstNode::StructDecl { name, fields } => todo!(),
|
||||
crate::AstNode::FieldDecl { name, field_type } => todo!(),
|
||||
crate::AstNode::FieldAccess { expr, field } => todo!(),
|
||||
crate::AstNode::UnresolvedDeclRef { name } => todo!(),
|
||||
crate::AstNode::DeclRef { decl } => todo!(),
|
||||
crate::AstNode::TypeDeclRef { ty } => todo!(),
|
||||
crate::AstNode::ExplicitCast { expr, ty } => todo!(),
|
||||
crate::AstNode::Deref { expr } => todo!(),
|
||||
crate::AstNode::AddressOf { expr } => todo!(),
|
||||
crate::AstNode::PlaceToValue { expr } => todo!(),
|
||||
crate::AstNode::ValueToPlace { expr } => todo!(),
|
||||
crate::AstNode::CallExpr { callee, arguments } => todo!(),
|
||||
crate::AstNode::Argument { expr } => todo!(),
|
||||
crate::AstNode::Not(index) => todo!(),
|
||||
crate::AstNode::Negate(index) => todo!(),
|
||||
crate::AstNode::Multiply { left, right } => todo!(),
|
||||
crate::AstNode::Divide { left, right } => todo!(),
|
||||
crate::AstNode::Modulus { left, right } => todo!(),
|
||||
crate::AstNode::Add { left, right } => todo!(),
|
||||
crate::AstNode::Subtract { left, right } => todo!(),
|
||||
crate::AstNode::BitOr { left, right } => todo!(),
|
||||
crate::AstNode::BitAnd { left, right } => todo!(),
|
||||
crate::AstNode::BitXor { left, right } => todo!(),
|
||||
crate::AstNode::LogicalOr { left, right } => todo!(),
|
||||
crate::AstNode::LogicalAnd { left, right } => todo!(),
|
||||
crate::AstNode::Eq { left, right } => todo!(),
|
||||
crate::AstNode::NotEq { left, right } => todo!(),
|
||||
crate::AstNode::Less { left, right } => todo!(),
|
||||
crate::AstNode::LessEq { left, right } => todo!(),
|
||||
crate::AstNode::Greater { left, right } => todo!(),
|
||||
crate::AstNode::GreaterEq { left, right } => todo!(),
|
||||
crate::AstNode::ShiftLeft { left, right } => todo!(),
|
||||
crate::AstNode::ShiftRight { left, right } => todo!(),
|
||||
crate::AstNode::Subscript { expr, index } => todo!(),
|
||||
crate::AstNode::If {
|
||||
condition,
|
||||
then,
|
||||
r#else,
|
||||
} => todo!(),
|
||||
crate::AstNode::Else { expr } => todo!(),
|
||||
crate::AstNode::Comment { text } => todo!(),
|
||||
crate::AstNode::Attributes { attrs } => todo!(),
|
||||
crate::AstNode::Doc { text } => todo!(),
|
||||
crate::AstNode::Error { err } => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
let mut node = Index(ast.nodes.len().checked_sub(1).map(|n| n as u32));
|
||||
}
|
||||
96
flake.lock
Normal file
96
flake.lock
Normal 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
31
flake.nix
Normal 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
204
src/ast2/biunification.rs
Normal 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
159
src/ast2/debug.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
|
|
@ -15,18 +15,27 @@ use crate::{
|
|||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[repr(u8)]
|
||||
pub enum SimpleType {
|
||||
F32 = 0,
|
||||
F32,
|
||||
F64,
|
||||
Bool,
|
||||
Void,
|
||||
USize,
|
||||
ISize,
|
||||
ComptimeInt,
|
||||
/// Top type: this is the supertype of all types, and any value can coerce into it.
|
||||
/// Although Rust's `()` is not a top type, it can be thought of as a top type in some contexts.
|
||||
Top,
|
||||
/// Bottom type: this is the subtype of all types, and it can coerce into a value of any type.
|
||||
/// Akin to Rust's `!`.
|
||||
Bottom,
|
||||
UInt(u16),
|
||||
SInt(u16),
|
||||
}
|
||||
|
||||
impl From<u8> for SimpleType {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
impl From<u32> for SimpleType {
|
||||
fn from(value: u32) -> Self {
|
||||
let [discriminant, bits] = *crate::common::u32_as_u16_slice(&value);
|
||||
match discriminant {
|
||||
0 => Self::F32,
|
||||
1 => Self::F64,
|
||||
2 => Self::Bool,
|
||||
|
|
@ -34,26 +43,49 @@ impl From<u8> for SimpleType {
|
|||
4 => Self::USize,
|
||||
5 => Self::ISize,
|
||||
6 => Self::ComptimeInt,
|
||||
7 => Self::Top,
|
||||
8 => Self::Bottom,
|
||||
9 => Self::UInt(bits),
|
||||
10 => Self::SInt(bits),
|
||||
_ => panic!("{value} is not a simple type"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SimpleType> for u32 {
|
||||
fn from(value: SimpleType) -> Self {
|
||||
match value {
|
||||
SimpleType::F32 => crate::common::u32_from_u16_slice(&[0, 0]),
|
||||
SimpleType::F64 => crate::common::u32_from_u16_slice(&[1, 0]),
|
||||
SimpleType::Bool => crate::common::u32_from_u16_slice(&[2, 0]),
|
||||
SimpleType::Void => crate::common::u32_from_u16_slice(&[3, 0]),
|
||||
SimpleType::USize => crate::common::u32_from_u16_slice(&[4, 0]),
|
||||
SimpleType::ISize => crate::common::u32_from_u16_slice(&[5, 0]),
|
||||
SimpleType::ComptimeInt => crate::common::u32_from_u16_slice(&[6, 0]),
|
||||
SimpleType::Top => crate::common::u32_from_u16_slice(&[7, 0]),
|
||||
SimpleType::Bottom => crate::common::u32_from_u16_slice(&[8, 0]),
|
||||
SimpleType::UInt(bits) => crate::common::u32_from_u16_slice(&[9, bits]),
|
||||
SimpleType::SInt(bits) => crate::common::u32_from_u16_slice(&[10, bits]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SimpleType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
SimpleType::F32 => "f32",
|
||||
SimpleType::F64 => "f64",
|
||||
SimpleType::Bool => "bool",
|
||||
SimpleType::Void => "void",
|
||||
SimpleType::USize => "usize",
|
||||
SimpleType::ISize => "isize",
|
||||
SimpleType::ComptimeInt => "comptime_int",
|
||||
}
|
||||
)
|
||||
let fmt: std::borrow::Cow<str> = match self {
|
||||
SimpleType::F32 => "f32".into(),
|
||||
SimpleType::F64 => "f64".into(),
|
||||
SimpleType::Bool => "bool".into(),
|
||||
SimpleType::Void => "void".into(),
|
||||
SimpleType::USize => "usize".into(),
|
||||
SimpleType::ISize => "isize".into(),
|
||||
SimpleType::ComptimeInt => "comptime_int".into(),
|
||||
SimpleType::Top => "⊤".into(),
|
||||
SimpleType::Bottom => "⊥".into(),
|
||||
SimpleType::UInt(bits) => format!("u{bits}").into(),
|
||||
SimpleType::SInt(bits) => format!("i{bits}").into(),
|
||||
};
|
||||
write!(f, "{fmt}",)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -70,8 +102,6 @@ pub enum Tag {
|
|||
F64,
|
||||
PositiveInt,
|
||||
NegativeInt,
|
||||
UIntType,
|
||||
SIntType,
|
||||
SimpleType,
|
||||
PointerType,
|
||||
ArrayType,
|
||||
|
|
@ -80,9 +110,9 @@ pub enum Tag {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
struct Item {
|
||||
tag: Tag,
|
||||
index: u32,
|
||||
pub(super) struct Item {
|
||||
pub(super) tag: Tag,
|
||||
pub(super) index: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
|
@ -118,12 +148,6 @@ pub enum Key<'a> {
|
|||
NegativeInt {
|
||||
bigint: BigInt,
|
||||
},
|
||||
UIntType {
|
||||
bit_width: u16,
|
||||
},
|
||||
SIntType {
|
||||
bit_width: u16,
|
||||
},
|
||||
SimpleType {
|
||||
ty: SimpleType,
|
||||
},
|
||||
|
|
@ -170,8 +194,6 @@ impl Display for KeyDisplay<'_> {
|
|||
Key::F64 { bits } => write!(f, "{bits}")?,
|
||||
Key::PositiveInt { ref bigint } => write!(f, "{bigint}")?,
|
||||
Key::NegativeInt { ref bigint } => write!(f, "{bigint}")?,
|
||||
Key::UIntType { bit_width } => write!(f, "u{bit_width}")?,
|
||||
Key::SIntType { bit_width } => write!(f, "i{bit_width}")?,
|
||||
Key::SimpleType { ty } => write!(f, "{ty}")?,
|
||||
Key::PointerType { pointee, flags } => {
|
||||
write!(f, "*{flags}{}", self.ip.display_key(pointee))?
|
||||
|
|
@ -230,8 +252,6 @@ impl Hash for Key<'_> {
|
|||
Key::F64 { bits } => ordered_float::OrderedFloat(*bits).hash(state),
|
||||
Key::PositiveInt { bigint } => bigint.hash(state),
|
||||
Key::NegativeInt { bigint } => bigint.hash(state),
|
||||
Key::UIntType { bit_width: bits } => bits.hash(state),
|
||||
Key::SIntType { bit_width: bits } => bits.hash(state),
|
||||
Key::SimpleType { ty } => ty.hash(state),
|
||||
Key::PointerType { pointee, flags } => (pointee, flags).hash(state),
|
||||
Key::ArrayType {
|
||||
|
|
@ -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 {
|
||||
tags: Vec<Tag>,
|
||||
indices: Vec<u32>,
|
||||
|
|
@ -460,6 +547,8 @@ macro_rules! static_keys {
|
|||
}
|
||||
|
||||
static_keys!(
|
||||
TOP => Key::SimpleType {ty: SimpleType::Top,},
|
||||
BOTTOM => Key::SimpleType {ty: SimpleType::Bottom,},
|
||||
BOOL => Key::SimpleType {ty: SimpleType::Bool,},
|
||||
F32 => Key::SimpleType {ty: SimpleType::F32,},
|
||||
F64 => Key::SimpleType {ty: SimpleType::F64,},
|
||||
|
|
@ -467,20 +556,20 @@ static_keys!(
|
|||
ISIZE => Key::SimpleType {ty: SimpleType::ISize,},
|
||||
VOID => Key::SimpleType {ty: SimpleType::Void,},
|
||||
COMPTIME_INT => Key::SimpleType {ty: SimpleType::ComptimeInt,},
|
||||
I1 => Key::SIntType { bit_width: 1 },
|
||||
U1 => Key::UIntType { bit_width: 1 },
|
||||
I0 => Key::SIntType { bit_width: 0 },
|
||||
U0 => Key::UIntType { bit_width: 0 },
|
||||
I8 => Key::SIntType { bit_width: 8 },
|
||||
U8 => Key::UIntType { bit_width: 8 },
|
||||
I16 => Key::SIntType { bit_width: 16 },
|
||||
U16 => Key::UIntType { bit_width: 16 },
|
||||
I32 => Key::SIntType { bit_width: 32 },
|
||||
U32 => Key::UIntType { bit_width: 32 },
|
||||
I64 => Key::SIntType { bit_width: 64 },
|
||||
U64 => Key::UIntType { bit_width: 64 },
|
||||
I128 => Key::SIntType { bit_width: 128 },
|
||||
U128 => Key::UIntType { bit_width: 128 },
|
||||
I0 => Key::SimpleType { ty: SimpleType::SInt(0) },
|
||||
U0 => Key::SimpleType { ty: SimpleType::UInt(0) },
|
||||
I1 => Key::SimpleType { ty: SimpleType::SInt(1) },
|
||||
U1 => Key::SimpleType { ty: SimpleType::UInt(1) },
|
||||
I8 => Key::SimpleType { ty: SimpleType::SInt(8) },
|
||||
U8 => Key::SimpleType { ty: SimpleType::UInt(8) },
|
||||
I16 => Key::SimpleType { ty: SimpleType::SInt(16) },
|
||||
U16 => Key::SimpleType { ty: SimpleType::UInt(16) },
|
||||
I32 => Key::SimpleType { ty: SimpleType::SInt(32) },
|
||||
U32 => Key::SimpleType { ty: SimpleType::UInt(32) },
|
||||
I64 => Key::SimpleType { ty: SimpleType::SInt(64) },
|
||||
U64 => Key::SimpleType { ty: SimpleType::UInt(64) },
|
||||
I128 => Key::SimpleType { ty: SimpleType::SInt(128) },
|
||||
U128 => Key::SimpleType { ty: SimpleType::UInt(128) },
|
||||
TRUE => Key::TrueValue,
|
||||
FALSE => Key::FalseValue,
|
||||
EMPTY_STRING => Key::String { str: "" },
|
||||
|
|
@ -529,41 +618,63 @@ impl InternPool {
|
|||
ty: SimpleType::ISize,
|
||||
})
|
||||
}
|
||||
// Assumes the type is present in the pool.
|
||||
fn get_simple_type_unchecked(&self, ty: SimpleType) -> Index {
|
||||
self.get_assume_present(&Key::SimpleType { ty })
|
||||
}
|
||||
pub fn get_u0_type(&self) -> Index {
|
||||
self.get_assume_present(&Key::UIntType { bit_width: 0 })
|
||||
self.get_simple_type_unchecked(SimpleType::UInt(0))
|
||||
}
|
||||
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 {
|
||||
self.get_assume_present(&Key::UIntType { bit_width: 1 })
|
||||
self.get_simple_type_unchecked(SimpleType::UInt(1))
|
||||
}
|
||||
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 {
|
||||
self.get_assume_present(&Key::UIntType { bit_width: 8 })
|
||||
self.get_simple_type_unchecked(SimpleType::UInt(8))
|
||||
}
|
||||
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 {
|
||||
self.get_assume_present(&Key::UIntType { bit_width: 16 })
|
||||
self.get_simple_type_unchecked(SimpleType::UInt(16))
|
||||
}
|
||||
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 {
|
||||
self.get_assume_present(&Key::UIntType { bit_width: 32 })
|
||||
self.get_simple_type_unchecked(SimpleType::UInt(32))
|
||||
}
|
||||
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 {
|
||||
self.get_assume_present(&Key::UIntType { bit_width: 64 })
|
||||
self.get_simple_type_unchecked(SimpleType::UInt(64))
|
||||
}
|
||||
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,
|
||||
}
|
||||
|
||||
impl TypeInfo {
|
||||
/// byte size
|
||||
pub fn size(&self) -> u32 {
|
||||
self.bitsize.div_ceil(8)
|
||||
}
|
||||
/// byte align
|
||||
pub fn align(&self) -> u32 {
|
||||
self.bitalign.div_ceil(8)
|
||||
}
|
||||
}
|
||||
|
||||
impl InternPool {
|
||||
pub fn peer_type(&mut self, lhs: Index, rhs: Index) -> Option<Index> {
|
||||
if lhs == rhs {
|
||||
|
|
@ -600,8 +722,9 @@ impl InternPool {
|
|||
| Key::SimpleType {
|
||||
ty: SimpleType::ISize,
|
||||
}
|
||||
| Key::SIntType { .. }
|
||||
| Key::UIntType { .. },
|
||||
| Key::SimpleType {
|
||||
ty: SimpleType::SInt(_) | SimpleType::UInt(_),
|
||||
},
|
||||
) => Some(rhs),
|
||||
(
|
||||
Key::SimpleType {
|
||||
|
|
@ -616,8 +739,9 @@ impl InternPool {
|
|||
| Key::SimpleType {
|
||||
ty: SimpleType::ISize,
|
||||
}
|
||||
| Key::SIntType { .. }
|
||||
| Key::UIntType { .. },
|
||||
| Key::SimpleType {
|
||||
ty: SimpleType::SInt(_) | SimpleType::UInt(_),
|
||||
},
|
||||
Key::SimpleType {
|
||||
ty: SimpleType::ComptimeInt,
|
||||
},
|
||||
|
|
@ -645,15 +769,6 @@ impl InternPool {
|
|||
pub fn to_mir_type(&self, index: Index, _ptr_size: TypeInfo) -> crate::mir::Type {
|
||||
use crate::mir::Type;
|
||||
match self.get_key(index) {
|
||||
Key::UIntType { bit_width: bits } => {
|
||||
let bits = bits as u32;
|
||||
|
||||
Type::from_bitsize_int(bits)
|
||||
}
|
||||
Key::SIntType { bit_width: bits } => {
|
||||
let bits = bits as u32;
|
||||
Type::from_bitsize_int(bits)
|
||||
}
|
||||
Key::SimpleType { ty } => match ty {
|
||||
SimpleType::F32 => Type::SinglePrecision,
|
||||
SimpleType::F64 => Type::DoublePrecision,
|
||||
|
|
@ -662,8 +777,11 @@ impl InternPool {
|
|||
todo!("void can't be turned into a mir type")
|
||||
}
|
||||
SimpleType::ISize | SimpleType::USize => Type::QWord,
|
||||
SimpleType::ComptimeInt => {
|
||||
panic!("comptime int can't be turned into a mir type")
|
||||
SimpleType::UInt(bits) | SimpleType::SInt(bits) => {
|
||||
Type::from_bitsize_int(bits as u32)
|
||||
}
|
||||
SimpleType::Top | SimpleType::Bottom | SimpleType::ComptimeInt => {
|
||||
panic!("{ty} can't be turned into a mir type")
|
||||
}
|
||||
},
|
||||
Key::ArrayType { .. } => {
|
||||
|
|
@ -682,11 +800,9 @@ impl InternPool {
|
|||
|
||||
pub fn is_type_signed(&self, index: Index, _ptr_size: TypeInfo) -> bool {
|
||||
match self.get_key(index) {
|
||||
Key::UIntType { .. } => false,
|
||||
Key::SIntType { .. } => true,
|
||||
Key::SimpleType { ty } => match ty {
|
||||
SimpleType::USize => false,
|
||||
SimpleType::ISize => true,
|
||||
SimpleType::USize | SimpleType::UInt(_) => false,
|
||||
SimpleType::ISize | SimpleType::SInt(_) => true,
|
||||
_ => false,
|
||||
},
|
||||
Key::PointerType { .. } => false,
|
||||
|
|
@ -699,23 +815,17 @@ impl InternPool {
|
|||
|
||||
pub fn size_of_type(&self, index: Index, ptr_size: TypeInfo) -> TypeInfo {
|
||||
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 {
|
||||
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 {
|
||||
bitsize: 32,
|
||||
bitalign: 32,
|
||||
|
|
@ -738,6 +848,9 @@ impl InternPool {
|
|||
},
|
||||
SimpleType::USize => ptr_size,
|
||||
SimpleType::ISize => ptr_size,
|
||||
SimpleType::Top | SimpleType::Bottom => {
|
||||
panic!("top and bottom types are not sized")
|
||||
}
|
||||
SimpleType::ComptimeInt => panic!("comptime int is unsized"),
|
||||
},
|
||||
Key::PointerType { .. } => ptr_size,
|
||||
|
|
@ -786,9 +899,13 @@ impl InternPool {
|
|||
crate::ast::Type::ComptimeNumber => self.get_comptime_int_type(),
|
||||
crate::ast::Type::Integer(i) => self.get_or_insert({
|
||||
if i.signed {
|
||||
Key::SIntType { bit_width: i.bits }
|
||||
Key::SimpleType {
|
||||
ty: SimpleType::SInt(i.bits),
|
||||
}
|
||||
} 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(),
|
||||
|
|
@ -827,9 +944,13 @@ impl InternPool {
|
|||
crate::ast::Type::ComptimeNumber => self.get_comptime_int_type(),
|
||||
crate::ast::Type::Integer(i) => self.get_assume_present(&{
|
||||
if i.signed {
|
||||
Key::SIntType { bit_width: i.bits }
|
||||
Key::SimpleType {
|
||||
ty: SimpleType::SInt(i.bits),
|
||||
}
|
||||
} 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(),
|
||||
|
|
@ -865,16 +986,19 @@ impl InternPool {
|
|||
pub fn as_ast1_type(&self, pointer_bits: u16, index: Index) -> crate::ast::Type {
|
||||
use crate::ast::Type;
|
||||
match self.get_key(index) {
|
||||
Key::UIntType { bit_width: bits } => Type::Integer(IntegralType::new(false, bits)),
|
||||
Key::SIntType { bit_width: bits } => Type::Integer(IntegralType::new(true, bits)),
|
||||
Key::SimpleType { ty } => match ty {
|
||||
SimpleType::F32 => Type::Floating(crate::ast::FloatingType::Binary32),
|
||||
SimpleType::F64 => Type::Floating(crate::ast::FloatingType::Binary64),
|
||||
SimpleType::SInt(bits) => Type::Integer(IntegralType::new(true, bits)),
|
||||
SimpleType::UInt(bits) => Type::Integer(IntegralType::new(false, bits)),
|
||||
SimpleType::Bool => Type::Bool,
|
||||
SimpleType::Void => Type::Void,
|
||||
SimpleType::USize => Type::Integer(IntegralType::new(false, pointer_bits)),
|
||||
SimpleType::ISize => Type::Integer(IntegralType::new(true, pointer_bits)),
|
||||
SimpleType::ComptimeInt => Type::comptime_number(),
|
||||
SimpleType::Top | SimpleType::Bottom => {
|
||||
panic!("top and bottom types cannot be converted to ast1 types")
|
||||
}
|
||||
},
|
||||
Key::PointerType { pointee, flags } => Type::Pointer {
|
||||
constness: flags.is_const,
|
||||
|
|
@ -992,9 +1116,7 @@ impl InternPool {
|
|||
self.create_item(Tag::NegativeInt, i)
|
||||
}
|
||||
|
||||
Key::UIntType { bit_width: bits } => self.create_item(Tag::UIntType, bits as u32),
|
||||
Key::SIntType { bit_width: bits } => self.create_item(Tag::SIntType, bits as u32),
|
||||
Key::SimpleType { ty } => self.create_item(Tag::SimpleType, ty as u8 as u32),
|
||||
Key::SimpleType { ty } => self.create_item(Tag::SimpleType, ty.into()),
|
||||
Key::PointerType { pointee, flags } => {
|
||||
let flags = flags.pack();
|
||||
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);
|
||||
Key::PositiveInt { bigint }
|
||||
}
|
||||
Tag::SIntType => Key::SIntType {
|
||||
bit_width: item.index as u16,
|
||||
},
|
||||
Tag::UIntType => Key::UIntType {
|
||||
bit_width: item.index as u16,
|
||||
},
|
||||
Tag::SimpleType => {
|
||||
let ty = item.idx() as u8;
|
||||
let ty = item.idx() as u32;
|
||||
|
||||
Key::SimpleType {
|
||||
ty: unsafe { core::mem::transmute::<u8, SimpleType>(ty) },
|
||||
ty: SimpleType::from(ty),
|
||||
}
|
||||
}
|
||||
Tag::PointerType => {
|
||||
|
|
@ -1231,8 +1347,12 @@ impl InternPool {
|
|||
|
||||
pub fn get_int_type(&mut self, signed: bool, bits: u16) -> Index {
|
||||
let key = match signed {
|
||||
true => Key::SIntType { bit_width: bits },
|
||||
false => Key::UIntType { bit_width: bits },
|
||||
true => Key::SimpleType {
|
||||
ty: SimpleType::SInt(bits),
|
||||
},
|
||||
false => Key::SimpleType {
|
||||
ty: SimpleType::UInt(bits),
|
||||
},
|
||||
};
|
||||
|
||||
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 {
|
||||
let key = Key::PointerType {
|
||||
pointee,
|
||||
|
|
@ -1304,6 +1438,7 @@ impl InternPool {
|
|||
};
|
||||
self.get_or_insert(key)
|
||||
}
|
||||
|
||||
pub fn try_get_pointer_type(
|
||||
&self,
|
||||
pointee: Index,
|
||||
|
|
@ -1414,7 +1549,7 @@ impl InternPool {
|
|||
((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 {
|
||||
tag: self.tags[i.index()],
|
||||
index: self.indices[i.index()],
|
||||
|
|
@ -1433,8 +1568,6 @@ impl InternPool {
|
|||
| Key::ArrayType { .. }
|
||||
| Key::PointerType { .. }
|
||||
| Key::SimpleType { .. }
|
||||
| Key::SIntType { .. }
|
||||
| Key::UIntType { .. }
|
||||
| Key::StructType { .. }
|
||||
)
|
||||
}
|
||||
|
|
|
|||
34
src/ast2/internable.rs
Normal file
34
src/ast2/internable.rs
Normal 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
1233
src/ast2/ir.rs
Normal file
File diff suppressed because it is too large
Load diff
2668
src/ast2/mod.rs
2668
src/ast2/mod.rs
File diff suppressed because it is too large
Load diff
1906
src/ast2/parser.rs
Normal file
1906
src/ast2/parser.rs
Normal file
File diff suppressed because it is too large
Load diff
631
src/ast2/tag.rs
Normal file
631
src/ast2/tag.rs
Normal 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
502
src/ast2/typecheck.rs
Normal 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
625
src/ast2/visitor.rs
Normal 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!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -55,7 +55,7 @@ fn main() {
|
|||
println!("AST:\n{buf}");
|
||||
}
|
||||
"ast2" => {
|
||||
let mut tree2 = compiler::ast2::ast_gen::Parser::new();
|
||||
let mut tree2 = compiler::ast2::parser::Parser::new();
|
||||
tree2.parse(tokens.iter());
|
||||
eprintln!("{tree2:#?}");
|
||||
println!("AST (new):\n{tree2}");
|
||||
|
|
|
|||
|
|
@ -72,9 +72,7 @@ pub fn is_oct_digit(ch: char) -> bool {
|
|||
}
|
||||
|
||||
pub fn is_hex_digit(ch: char) -> bool {
|
||||
('0'..='9').contains(&ch)
|
||||
|| ('a'..='f').contains(&ch)
|
||||
|| ('A'..='F').contains(&ch)
|
||||
('0'..='9').contains(&ch) || ('a'..='f').contains(&ch) || ('A'..='F').contains(&ch)
|
||||
}
|
||||
|
||||
/// Trait for only yielding the next item in the Iterator if it tests true for some predicate
|
||||
|
|
@ -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) {
|
||||
(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
|
||||
}
|
||||
|
|
|
|||
424
src/comptime.rs
424
src/comptime.rs
|
|
@ -12,10 +12,7 @@ pub mod bigint {
|
|||
pub struct BigInt(Vec<u32>);
|
||||
|
||||
impl BigInt {
|
||||
pub fn parse_digits<C: IntoIterator<Item = char>>(
|
||||
text: C,
|
||||
radix: Radix,
|
||||
) -> BigInt {
|
||||
pub fn parse_digits<C: IntoIterator<Item = char>>(text: C, radix: Radix) -> BigInt {
|
||||
Self(parse_bigint(text.into_iter(), radix))
|
||||
}
|
||||
pub fn from_u32(v: u32) -> BigInt {
|
||||
|
|
@ -240,13 +237,9 @@ pub mod bigint {
|
|||
|
||||
fn sub(mut self, rhs: Self) -> Self::Output {
|
||||
if self.0.len() < rhs.0.len() {
|
||||
println!(
|
||||
"extending self by {} zeroes",
|
||||
rhs.0.len() - self.0.len()
|
||||
);
|
||||
self.0.extend(
|
||||
core::iter::repeat(0).take(rhs.0.len() - self.0.len()),
|
||||
);
|
||||
println!("extending self by {} zeroes", rhs.0.len() - self.0.len());
|
||||
self.0
|
||||
.extend(core::iter::repeat(0).take(rhs.0.len() - self.0.len()));
|
||||
println!("self: {self:?}");
|
||||
}
|
||||
sub_bigint(&mut self.0, &rhs.0);
|
||||
|
|
@ -504,10 +497,7 @@ pub mod bigint {
|
|||
_ => {
|
||||
if scalar.is_power_of_two() {
|
||||
lhs.push(0);
|
||||
shl_bitint(
|
||||
lhs.as_mut_slice(),
|
||||
scalar.trailing_zeros() as usize,
|
||||
);
|
||||
shl_bitint(lhs.as_mut_slice(), scalar.trailing_zeros() as usize);
|
||||
} else {
|
||||
let mut carry = 0;
|
||||
for a in lhs.iter_mut() {
|
||||
|
|
@ -580,7 +570,7 @@ pub mod bigint {
|
|||
fn trailing_zeros(lhs: &[u32]) -> usize {
|
||||
lhs.iter()
|
||||
.enumerate()
|
||||
.find(|(_, &c)| c != 0)
|
||||
.find(|&(_, &c)| c != 0)
|
||||
.map(|(i, &c)| i * u32::BITS as usize + c.trailing_zeros() as usize)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
|
@ -593,10 +583,7 @@ pub mod bigint {
|
|||
#[allow(dead_code)]
|
||||
/// divident must be at least as wide as divisor
|
||||
/// returns (quotient, remainder)
|
||||
pub fn div_rem_bigint_ref(
|
||||
divident: &BigInt,
|
||||
divisor: &BigInt,
|
||||
) -> (BigInt, BigInt) {
|
||||
pub fn div_rem_bigint_ref(divident: &BigInt, divisor: &BigInt) -> (BigInt, BigInt) {
|
||||
if bigint_is_zero(&divisor.0) {
|
||||
panic!("divide by zero!");
|
||||
}
|
||||
|
|
@ -614,8 +601,7 @@ pub mod bigint {
|
|||
|
||||
if divisor.is_power_of_two() {
|
||||
let exp = divisor.trailing_zeros();
|
||||
let (div, rem) =
|
||||
divident.0.split_at(exp.div_floor(u32::BITS as usize));
|
||||
let (div, rem) = divident.0.split_at(exp.div_floor(u32::BITS as usize));
|
||||
let (mut div, mut rem) = (div.to_vec(), rem.to_vec());
|
||||
|
||||
shr_bitint(&mut div, exp % u32::BITS as usize);
|
||||
|
|
@ -641,10 +627,7 @@ pub mod bigint {
|
|||
if shift == 0 {
|
||||
div_rem_core(divident.clone(), &divisor.0)
|
||||
} else {
|
||||
let (q, r) = div_rem_core(
|
||||
divident.clone() << shift,
|
||||
&(divisor.clone() << shift).0,
|
||||
);
|
||||
let (q, r) = div_rem_core(divident.clone() << shift, &(divisor.clone() << shift).0);
|
||||
|
||||
(q, r >> shift)
|
||||
}
|
||||
|
|
@ -653,10 +636,7 @@ pub mod bigint {
|
|||
#[allow(dead_code)]
|
||||
/// divident must be at least as wide as divisor
|
||||
/// returns (quotient, remainder)
|
||||
pub fn div_rem_bigint(
|
||||
divident: BigInt,
|
||||
divisor: BigInt,
|
||||
) -> (BigInt, BigInt) {
|
||||
pub fn div_rem_bigint(divident: BigInt, divisor: BigInt) -> (BigInt, BigInt) {
|
||||
let divident = divident.normalised();
|
||||
let mut divisor = divisor.normalised();
|
||||
|
||||
|
|
@ -677,8 +657,7 @@ pub mod bigint {
|
|||
|
||||
if divisor.is_power_of_two() {
|
||||
let exp = divisor.trailing_zeros();
|
||||
let (div, rem) =
|
||||
divident.0.split_at(exp.div_floor(u32::BITS as usize));
|
||||
let (div, rem) = divident.0.split_at(exp.div_floor(u32::BITS as usize));
|
||||
let (mut div, mut rem) = (div.to_vec(), rem.to_vec());
|
||||
|
||||
shr_bitint(&mut div, exp % u32::BITS as usize);
|
||||
|
|
@ -810,9 +789,7 @@ pub mod bigint {
|
|||
// q0 is too large if:
|
||||
// [a2,a1,a0] < q0 * [b1,b0]
|
||||
// (r << BITS) + a2 < q0 * b1
|
||||
while r <= u32::MAX as u64
|
||||
&& from_lo_hi_dwords(r as u32, a2) < q0 as u64 * b1 as u64
|
||||
{
|
||||
while r <= u32::MAX as u64 && from_lo_hi_dwords(r as u32, a2) < q0 as u64 * b1 as u64 {
|
||||
q0 -= 1;
|
||||
r += b0 as u64;
|
||||
}
|
||||
|
|
@ -1041,10 +1018,7 @@ pub mod bigint {
|
|||
carry
|
||||
}
|
||||
|
||||
pub fn parse_bigint(
|
||||
text: impl Iterator<Item = char>,
|
||||
radix: Radix,
|
||||
) -> Vec<u32> {
|
||||
pub fn parse_bigint(text: impl Iterator<Item = char>, radix: Radix) -> Vec<u32> {
|
||||
let digits = text
|
||||
.filter_map(|c| match c {
|
||||
'_' => None,
|
||||
|
|
@ -1112,20 +1086,14 @@ pub mod bigint {
|
|||
|
||||
#[test]
|
||||
fn parse() {
|
||||
let bigint = BigInt::parse_digits(
|
||||
"2_cafe_babe_dead_beef".chars(),
|
||||
Radix::Hex,
|
||||
);
|
||||
let bigint = BigInt::parse_digits("2_cafe_babe_dead_beef".chars(), Radix::Hex);
|
||||
println!("{:#x?}", bigint);
|
||||
let bigint = BigInt::parse_digits("f".chars(), Radix::Hex);
|
||||
println!("{:#x?}", bigint);
|
||||
}
|
||||
#[test]
|
||||
fn add() {
|
||||
let a = BigInt::parse_digits(
|
||||
"2_0000_0000_0000_0000".chars(),
|
||||
Radix::Hex,
|
||||
);
|
||||
let a = BigInt::parse_digits("2_0000_0000_0000_0000".chars(), Radix::Hex);
|
||||
println!("{:#x?}", a);
|
||||
let b = BigInt::parse_digits("cafebabe".chars(), Radix::Hex);
|
||||
println!("{:#x?}", b);
|
||||
|
|
@ -1143,10 +1111,7 @@ pub mod bigint {
|
|||
}
|
||||
#[test]
|
||||
fn overflowing_sub() {
|
||||
let a = BigInt::parse_digits(
|
||||
"2_0000_0000_0000_0000".chars(),
|
||||
Radix::Hex,
|
||||
);
|
||||
let a = BigInt::parse_digits("2_0000_0000_0000_0000".chars(), Radix::Hex);
|
||||
println!("{:#x?}", a);
|
||||
let b = BigInt::parse_digits("ffff_ffff".chars(), Radix::Hex);
|
||||
println!("{:#x?}", b);
|
||||
|
|
@ -1155,8 +1120,7 @@ pub mod bigint {
|
|||
}
|
||||
#[test]
|
||||
fn shr() {
|
||||
let mut a =
|
||||
BigInt::parse_digits("cafe_babe_0000".chars(), Radix::Hex);
|
||||
let mut a = BigInt::parse_digits("cafe_babe_0000".chars(), Radix::Hex);
|
||||
print!("{:0>8x?} >> 32 ", a);
|
||||
shr_bitint(&mut a.0, 32);
|
||||
println!("{:0>8x?}", a);
|
||||
|
|
@ -1188,9 +1152,7 @@ pub mod bigint {
|
|||
pub mod bigsint {
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
ops::{
|
||||
Add, AddAssign, Div, Mul, Neg, Not, Rem, Shl, Shr, Sub, SubAssign,
|
||||
},
|
||||
ops::{Add, AddAssign, Div, Mul, Neg, Not, Rem, Shl, Shr, Sub, SubAssign},
|
||||
};
|
||||
|
||||
use super::bigint::{self, *};
|
||||
|
|
@ -1333,13 +1295,11 @@ pub mod bigsint {
|
|||
match (self.sign, rhs.sign) {
|
||||
(_, Sign::None) => self,
|
||||
(Sign::None, _) => rhs,
|
||||
(Sign::Positive, Sign::Positive)
|
||||
| (Sign::Negative, Sign::Negative) => Self {
|
||||
(Sign::Positive, Sign::Positive) | (Sign::Negative, Sign::Negative) => Self {
|
||||
sign: self.sign,
|
||||
bigint: self.bigint + rhs.bigint,
|
||||
},
|
||||
(Sign::Positive, Sign::Negative)
|
||||
| (Sign::Negative, Sign::Positive) => {
|
||||
(Sign::Positive, Sign::Negative) | (Sign::Negative, Sign::Positive) => {
|
||||
match self.bigint.cmp(&rhs.bigint) {
|
||||
Ordering::Less => Self {
|
||||
sign: rhs.sign,
|
||||
|
|
@ -1361,13 +1321,11 @@ pub mod bigsint {
|
|||
|
||||
fn add(self, rhs: u32) -> Self::Output {
|
||||
match self.sign {
|
||||
Sign::Negative => {
|
||||
match self.bigint.partial_cmp(&rhs).unwrap() {
|
||||
Ordering::Less => Self::positive(rhs - self.bigint),
|
||||
Ordering::Equal => Self::zero(),
|
||||
Ordering::Greater => -Self::positive(self.bigint - rhs),
|
||||
}
|
||||
}
|
||||
Sign::Negative => match self.bigint.partial_cmp(&rhs).unwrap() {
|
||||
Ordering::Less => Self::positive(rhs - self.bigint),
|
||||
Ordering::Equal => Self::zero(),
|
||||
Ordering::Greater => -Self::positive(self.bigint - rhs),
|
||||
},
|
||||
Sign::None => Self::from_u32(rhs),
|
||||
Sign::Positive => Self::positive(self.bigint + rhs),
|
||||
}
|
||||
|
|
@ -1379,13 +1337,11 @@ pub mod bigsint {
|
|||
|
||||
fn add(self, rhs: u64) -> Self::Output {
|
||||
match self.sign {
|
||||
Sign::Negative => {
|
||||
match self.bigint.partial_cmp(&rhs).unwrap() {
|
||||
Ordering::Less => Self::positive(rhs - self.bigint),
|
||||
Ordering::Equal => Self::zero(),
|
||||
Ordering::Greater => -Self::positive(self.bigint - rhs),
|
||||
}
|
||||
}
|
||||
Sign::Negative => match self.bigint.partial_cmp(&rhs).unwrap() {
|
||||
Ordering::Less => Self::positive(rhs - self.bigint),
|
||||
Ordering::Equal => Self::zero(),
|
||||
Ordering::Greater => -Self::positive(self.bigint - rhs),
|
||||
},
|
||||
Sign::None => Self::from_u64(rhs),
|
||||
Sign::Positive => Self::positive(self.bigint + rhs),
|
||||
}
|
||||
|
|
@ -1406,13 +1362,11 @@ pub mod bigsint {
|
|||
match (self.sign, rhs.sign) {
|
||||
(_, Sign::None) => self,
|
||||
(Sign::None, _) => -rhs,
|
||||
(Sign::Positive, Sign::Negative)
|
||||
| (Sign::Negative, Sign::Positive) => Self {
|
||||
(Sign::Positive, Sign::Negative) | (Sign::Negative, Sign::Positive) => Self {
|
||||
sign: self.sign,
|
||||
bigint: self.bigint + rhs.bigint,
|
||||
},
|
||||
(Sign::Positive, Sign::Positive)
|
||||
| (Sign::Negative, Sign::Negative) => {
|
||||
(Sign::Positive, Sign::Positive) | (Sign::Negative, Sign::Negative) => {
|
||||
match self.bigint.cmp(&rhs.bigint) {
|
||||
Ordering::Less => Self {
|
||||
sign: -self.sign,
|
||||
|
|
@ -1516,11 +1470,7 @@ pub mod bigsint {
|
|||
bigint: r,
|
||||
};
|
||||
|
||||
if rhs.is_negative() {
|
||||
(-q, r)
|
||||
} else {
|
||||
(q, r)
|
||||
}
|
||||
if rhs.is_negative() { (-q, r) } else { (q, r) }
|
||||
}
|
||||
|
||||
fn shr_rounding(lhs: &BigSInt, shift: usize) -> bool {
|
||||
|
|
@ -1548,7 +1498,7 @@ use std::{
|
|||
};
|
||||
|
||||
use num_bigint::{BigInt, BigUint, Sign};
|
||||
use num_traits::{cast::ToPrimitive, ToBytes};
|
||||
use num_traits::{ToBytes, cast::ToPrimitive};
|
||||
|
||||
use crate::ast::{FloatingType, IntegralType, Type};
|
||||
|
||||
|
|
@ -1585,20 +1535,14 @@ impl ComptimeInt {
|
|||
pub fn add(self, other: Self) -> Result<Self> {
|
||||
let (a, b) = self.coalesce(other)?;
|
||||
match (a, b) {
|
||||
(
|
||||
ComptimeInt::Native { bits: a, ty },
|
||||
ComptimeInt::Native { bits: b, .. },
|
||||
) => {
|
||||
(ComptimeInt::Native { bits: a, ty }, ComptimeInt::Native { bits: b, .. }) => {
|
||||
let bits = a.checked_add(b).ok_or(Error::IntegerOverflow)?;
|
||||
if bits & !ty.u128_bitmask() != 0 {
|
||||
return Err(Error::IntegerOverflow);
|
||||
}
|
||||
Ok(Self::Native { bits, ty })
|
||||
}
|
||||
(
|
||||
ComptimeInt::BigInt { bits: a, ty },
|
||||
ComptimeInt::BigInt { bits: b, .. },
|
||||
) => {
|
||||
(ComptimeInt::BigInt { bits: a, ty }, ComptimeInt::BigInt { bits: b, .. }) => {
|
||||
let width = ty.bits - ty.signed as u16;
|
||||
let bits = a + b;
|
||||
if bits.bits() > width as u64 {
|
||||
|
|
@ -1607,9 +1551,7 @@ impl ComptimeInt {
|
|||
Ok(Self::BigInt { bits, ty })
|
||||
}
|
||||
}
|
||||
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => {
|
||||
Ok(Self::Comptime(a + b))
|
||||
}
|
||||
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => Ok(Self::Comptime(a + b)),
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
|
|
@ -1619,20 +1561,14 @@ impl ComptimeInt {
|
|||
pub fn sub(self, other: Self) -> Result<Self> {
|
||||
let (a, b) = self.coalesce(other)?;
|
||||
match (a, b) {
|
||||
(
|
||||
ComptimeInt::Native { bits: a, ty },
|
||||
ComptimeInt::Native { bits: b, .. },
|
||||
) => {
|
||||
(ComptimeInt::Native { bits: a, ty }, ComptimeInt::Native { bits: b, .. }) => {
|
||||
let bits = a.checked_sub(b).ok_or(Error::IntegerOverflow)?;
|
||||
if bits & !ty.u128_bitmask() != 0 {
|
||||
return Err(Error::IntegerOverflow);
|
||||
}
|
||||
Ok(Self::Native { bits, ty })
|
||||
}
|
||||
(
|
||||
ComptimeInt::BigInt { bits: a, ty },
|
||||
ComptimeInt::BigInt { bits: b, .. },
|
||||
) => {
|
||||
(ComptimeInt::BigInt { bits: a, ty }, ComptimeInt::BigInt { bits: b, .. }) => {
|
||||
let width = ty.bits - ty.signed as u16;
|
||||
let bits = a - b;
|
||||
if bits.bits() > width as u64 {
|
||||
|
|
@ -1641,9 +1577,7 @@ impl ComptimeInt {
|
|||
Ok(Self::BigInt { bits, ty })
|
||||
}
|
||||
}
|
||||
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => {
|
||||
Ok(Self::Comptime(a - b))
|
||||
}
|
||||
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => Ok(Self::Comptime(a - b)),
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
|
|
@ -1653,20 +1587,14 @@ impl ComptimeInt {
|
|||
pub fn mul(self, other: Self) -> Result<Self> {
|
||||
let (a, b) = self.coalesce(other)?;
|
||||
match (a, b) {
|
||||
(
|
||||
ComptimeInt::Native { bits: a, ty },
|
||||
ComptimeInt::Native { bits: b, .. },
|
||||
) => {
|
||||
(ComptimeInt::Native { bits: a, ty }, ComptimeInt::Native { bits: b, .. }) => {
|
||||
let bits = a.checked_mul(b).ok_or(Error::IntegerOverflow)?;
|
||||
if bits & !ty.u128_bitmask() != 0 {
|
||||
return Err(Error::IntegerOverflow);
|
||||
}
|
||||
Ok(Self::Native { bits, ty })
|
||||
}
|
||||
(
|
||||
ComptimeInt::BigInt { bits: a, ty },
|
||||
ComptimeInt::BigInt { bits: b, .. },
|
||||
) => {
|
||||
(ComptimeInt::BigInt { bits: a, ty }, ComptimeInt::BigInt { bits: b, .. }) => {
|
||||
let width = ty.bits - ty.signed as u16;
|
||||
let bits = a * b;
|
||||
if bits.bits() > width as u64 {
|
||||
|
|
@ -1675,9 +1603,7 @@ impl ComptimeInt {
|
|||
Ok(Self::BigInt { bits, ty })
|
||||
}
|
||||
}
|
||||
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => {
|
||||
Ok(Self::Comptime(a * b))
|
||||
}
|
||||
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => Ok(Self::Comptime(a * b)),
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
|
|
@ -1687,20 +1613,14 @@ impl ComptimeInt {
|
|||
pub fn div(self, other: Self) -> Result<Self> {
|
||||
let (a, b) = self.coalesce(other)?;
|
||||
match (a, b) {
|
||||
(
|
||||
ComptimeInt::Native { bits: a, ty },
|
||||
ComptimeInt::Native { bits: b, .. },
|
||||
) => {
|
||||
(ComptimeInt::Native { bits: a, ty }, ComptimeInt::Native { bits: b, .. }) => {
|
||||
let bits = a.checked_div(b).ok_or(Error::IntegerOverflow)?;
|
||||
if bits & !ty.u128_bitmask() != 0 {
|
||||
return Err(Error::IntegerOverflow);
|
||||
}
|
||||
Ok(Self::Native { bits, ty })
|
||||
}
|
||||
(
|
||||
ComptimeInt::BigInt { bits: a, ty },
|
||||
ComptimeInt::BigInt { bits: b, .. },
|
||||
) => {
|
||||
(ComptimeInt::BigInt { bits: a, ty }, ComptimeInt::BigInt { bits: b, .. }) => {
|
||||
let width = ty.bits - ty.signed as u16;
|
||||
let bits = a / b;
|
||||
if bits.bits() > width as u64 {
|
||||
|
|
@ -1709,9 +1629,7 @@ impl ComptimeInt {
|
|||
Ok(Self::BigInt { bits, ty })
|
||||
}
|
||||
}
|
||||
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => {
|
||||
Ok(Self::Comptime(a / b))
|
||||
}
|
||||
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => Ok(Self::Comptime(a / b)),
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
|
|
@ -1721,20 +1639,14 @@ impl ComptimeInt {
|
|||
pub fn rem(self, other: Self) -> Result<Self> {
|
||||
let (a, b) = self.coalesce(other)?;
|
||||
match (a, b) {
|
||||
(
|
||||
ComptimeInt::Native { bits: a, ty },
|
||||
ComptimeInt::Native { bits: b, .. },
|
||||
) => {
|
||||
(ComptimeInt::Native { bits: a, ty }, ComptimeInt::Native { bits: b, .. }) => {
|
||||
let bits = a.checked_rem(b).ok_or(Error::IntegerOverflow)?;
|
||||
if bits & !ty.u128_bitmask() != 0 {
|
||||
return Err(Error::IntegerOverflow);
|
||||
}
|
||||
Ok(Self::Native { bits, ty })
|
||||
}
|
||||
(
|
||||
ComptimeInt::BigInt { bits: a, ty },
|
||||
ComptimeInt::BigInt { bits: b, .. },
|
||||
) => {
|
||||
(ComptimeInt::BigInt { bits: a, ty }, ComptimeInt::BigInt { bits: b, .. }) => {
|
||||
let width = ty.bits - ty.signed as u16;
|
||||
let bits = a % b;
|
||||
if bits.bits() > width as u64 {
|
||||
|
|
@ -1743,9 +1655,7 @@ impl ComptimeInt {
|
|||
Ok(Self::BigInt { bits, ty })
|
||||
}
|
||||
}
|
||||
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => {
|
||||
Ok(Self::Comptime(a % b))
|
||||
}
|
||||
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => Ok(Self::Comptime(a % b)),
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
|
|
@ -1755,23 +1665,15 @@ impl ComptimeInt {
|
|||
pub fn bitand(self, other: Self) -> Result<Self> {
|
||||
let (a, b) = self.coalesce(other)?;
|
||||
match (a, b) {
|
||||
(
|
||||
ComptimeInt::Native { bits: a, ty },
|
||||
ComptimeInt::Native { bits: b, .. },
|
||||
) => {
|
||||
(ComptimeInt::Native { bits: a, ty }, ComptimeInt::Native { bits: b, .. }) => {
|
||||
let bits = a.bitand(b);
|
||||
Ok(Self::Native { bits, ty })
|
||||
}
|
||||
(
|
||||
ComptimeInt::BigInt { bits: a, ty },
|
||||
ComptimeInt::BigInt { bits: b, .. },
|
||||
) => {
|
||||
(ComptimeInt::BigInt { bits: a, ty }, ComptimeInt::BigInt { bits: b, .. }) => {
|
||||
let bits = a & b;
|
||||
Ok(Self::BigInt { bits, ty })
|
||||
}
|
||||
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => {
|
||||
Ok(Self::Comptime(a & b))
|
||||
}
|
||||
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => Ok(Self::Comptime(a & b)),
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
|
|
@ -1781,23 +1683,15 @@ impl ComptimeInt {
|
|||
pub fn bitor(self, other: Self) -> Result<Self> {
|
||||
let (a, b) = self.coalesce(other)?;
|
||||
match (a, b) {
|
||||
(
|
||||
ComptimeInt::Native { bits: a, ty },
|
||||
ComptimeInt::Native { bits: b, .. },
|
||||
) => {
|
||||
(ComptimeInt::Native { bits: a, ty }, ComptimeInt::Native { bits: b, .. }) => {
|
||||
let bits = a.bitor(b);
|
||||
Ok(Self::Native { bits, ty })
|
||||
}
|
||||
(
|
||||
ComptimeInt::BigInt { bits: a, ty },
|
||||
ComptimeInt::BigInt { bits: b, .. },
|
||||
) => {
|
||||
(ComptimeInt::BigInt { bits: a, ty }, ComptimeInt::BigInt { bits: b, .. }) => {
|
||||
let bits = a | b;
|
||||
Ok(Self::BigInt { bits, ty })
|
||||
}
|
||||
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => {
|
||||
Ok(Self::Comptime(a | b))
|
||||
}
|
||||
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => Ok(Self::Comptime(a | b)),
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
|
|
@ -1807,23 +1701,15 @@ impl ComptimeInt {
|
|||
pub fn bitxor(self, other: Self) -> Result<Self> {
|
||||
let (a, b) = self.coalesce(other)?;
|
||||
match (a, b) {
|
||||
(
|
||||
ComptimeInt::Native { bits: a, ty },
|
||||
ComptimeInt::Native { bits: b, .. },
|
||||
) => {
|
||||
(ComptimeInt::Native { bits: a, ty }, ComptimeInt::Native { bits: b, .. }) => {
|
||||
let bits = a.bitxor(b);
|
||||
Ok(Self::Native { bits, ty })
|
||||
}
|
||||
(
|
||||
ComptimeInt::BigInt { bits: a, ty },
|
||||
ComptimeInt::BigInt { bits: b, .. },
|
||||
) => {
|
||||
(ComptimeInt::BigInt { bits: a, ty }, ComptimeInt::BigInt { bits: b, .. }) => {
|
||||
let bits = a ^ b;
|
||||
Ok(Self::BigInt { bits, ty })
|
||||
}
|
||||
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => {
|
||||
Ok(Self::Comptime(a ^ b))
|
||||
}
|
||||
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => Ok(Self::Comptime(a ^ b)),
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
|
|
@ -1832,14 +1718,8 @@ impl ComptimeInt {
|
|||
pub fn cmp(self, other: Self) -> Result<Ordering> {
|
||||
let (a, b) = self.coalesce(other)?;
|
||||
let ord = match (a, b) {
|
||||
(
|
||||
ComptimeInt::Native { bits: a, .. },
|
||||
ComptimeInt::Native { bits: b, .. },
|
||||
) => a.cmp(&b),
|
||||
(
|
||||
ComptimeInt::BigInt { bits: a, .. },
|
||||
ComptimeInt::BigInt { bits: b, .. },
|
||||
) => a.cmp(&b),
|
||||
(ComptimeInt::Native { bits: a, .. }, ComptimeInt::Native { bits: b, .. }) => a.cmp(&b),
|
||||
(ComptimeInt::BigInt { bits: a, .. }, ComptimeInt::BigInt { bits: b, .. }) => a.cmp(&b),
|
||||
(ComptimeInt::Comptime(a), ComptimeInt::Comptime(b)) => a.cmp(&b),
|
||||
_ => {
|
||||
unreachable!()
|
||||
|
|
@ -1857,13 +1737,11 @@ impl ComptimeInt {
|
|||
let bits = if ty.signed {
|
||||
(bits as i128)
|
||||
.checked_shl(shift)
|
||||
.ok_or(Error::IntegerOverflow)?
|
||||
as u128
|
||||
.ok_or(Error::IntegerOverflow)? as u128
|
||||
} else {
|
||||
(bits as u128)
|
||||
.checked_shl(shift)
|
||||
.ok_or(Error::IntegerOverflow)?
|
||||
as u128
|
||||
.ok_or(Error::IntegerOverflow)? as u128
|
||||
} & ty.u128_bitmask();
|
||||
|
||||
Ok(Self::Native { bits, ty })
|
||||
|
|
@ -1888,13 +1766,11 @@ impl ComptimeInt {
|
|||
let bits = if ty.signed {
|
||||
(bits as i128)
|
||||
.checked_shr(shift)
|
||||
.ok_or(Error::IntegerOverflow)?
|
||||
as u128
|
||||
.ok_or(Error::IntegerOverflow)? as u128
|
||||
} else {
|
||||
(bits as u128)
|
||||
.checked_shr(shift)
|
||||
.ok_or(Error::IntegerOverflow)?
|
||||
as u128
|
||||
.ok_or(Error::IntegerOverflow)? as u128
|
||||
};
|
||||
|
||||
Ok(Self::Native { bits, ty })
|
||||
|
|
@ -1913,9 +1789,7 @@ impl ComptimeInt {
|
|||
if ty.signed {
|
||||
return Err(Error::UnsignedNegation);
|
||||
}
|
||||
let bits =
|
||||
(a as i128).checked_neg().ok_or(Error::IntegerOverflow)?
|
||||
as u128;
|
||||
let bits = (a as i128).checked_neg().ok_or(Error::IntegerOverflow)? as u128;
|
||||
|
||||
if bits & !ty.u128_bitmask() != 0 {
|
||||
return Err(Error::IntegerOverflow);
|
||||
|
|
@ -1933,9 +1807,7 @@ impl ComptimeInt {
|
|||
bits: !bits | ty.u128_bitmask(),
|
||||
ty,
|
||||
}),
|
||||
ComptimeInt::BigInt { bits, ty } => {
|
||||
Ok(Self::BigInt { bits: !bits, ty })
|
||||
}
|
||||
ComptimeInt::BigInt { bits, ty } => Ok(Self::BigInt { bits: !bits, ty }),
|
||||
ComptimeInt::Comptime(bigint) => Ok(Self::Comptime(!bigint)),
|
||||
}
|
||||
}
|
||||
|
|
@ -1951,14 +1823,8 @@ impl ComptimeInt {
|
|||
|
||||
fn coalesce(self, other: Self) -> Result<(ComptimeInt, ComptimeInt)> {
|
||||
match (self, other) {
|
||||
(
|
||||
lhs @ ComptimeInt::Native { ty: a_ty, .. },
|
||||
ComptimeInt::Comptime(b),
|
||||
)
|
||||
| (
|
||||
lhs @ ComptimeInt::Native { ty: a_ty, .. },
|
||||
ComptimeInt::BigInt { bits: b, .. },
|
||||
) => {
|
||||
(lhs @ ComptimeInt::Native { ty: a_ty, .. }, ComptimeInt::Comptime(b))
|
||||
| (lhs @ ComptimeInt::Native { ty: a_ty, .. }, ComptimeInt::BigInt { bits: b, .. }) => {
|
||||
let b_signed = b.sign() == Sign::Minus;
|
||||
if !a_ty.signed && b_signed {
|
||||
return Err(Error::IncompatibleTypes);
|
||||
|
|
@ -1975,14 +1841,8 @@ impl ComptimeInt {
|
|||
};
|
||||
Ok((lhs, Self::Native { bits: b, ty: a_ty }))
|
||||
}
|
||||
(
|
||||
ComptimeInt::Comptime(b),
|
||||
rhs @ ComptimeInt::Native { ty: a_ty, .. },
|
||||
)
|
||||
| (
|
||||
ComptimeInt::BigInt { bits: b, .. },
|
||||
rhs @ ComptimeInt::Native { ty: a_ty, .. },
|
||||
) => {
|
||||
(ComptimeInt::Comptime(b), rhs @ ComptimeInt::Native { ty: a_ty, .. })
|
||||
| (ComptimeInt::BigInt { bits: b, .. }, rhs @ ComptimeInt::Native { ty: a_ty, .. }) => {
|
||||
let b_signed = b.sign() == Sign::Minus;
|
||||
if !a_ty.signed && b_signed {
|
||||
return Err(Error::IncompatibleTypes);
|
||||
|
|
@ -1999,10 +1859,7 @@ impl ComptimeInt {
|
|||
};
|
||||
Ok((Self::Native { bits: b, ty: a_ty }, rhs))
|
||||
}
|
||||
(
|
||||
lhs @ ComptimeInt::BigInt { ty, .. },
|
||||
ComptimeInt::Comptime(b),
|
||||
) => {
|
||||
(lhs @ ComptimeInt::BigInt { ty, .. }, ComptimeInt::Comptime(b)) => {
|
||||
let b_signed = b.sign() == Sign::Minus;
|
||||
if !ty.signed && b_signed {
|
||||
return Err(Error::IncompatibleTypes);
|
||||
|
|
@ -2014,10 +1871,7 @@ impl ComptimeInt {
|
|||
}
|
||||
Ok((lhs, Self::BigInt { bits: b, ty }))
|
||||
}
|
||||
(
|
||||
ComptimeInt::Comptime(b),
|
||||
rhs @ ComptimeInt::BigInt { ty, .. },
|
||||
) => {
|
||||
(ComptimeInt::Comptime(b), rhs @ ComptimeInt::BigInt { ty, .. }) => {
|
||||
let b_signed = b.sign() == Sign::Minus;
|
||||
if !ty.signed && b_signed {
|
||||
return Err(Error::IncompatibleTypes);
|
||||
|
|
@ -2029,20 +1883,14 @@ impl ComptimeInt {
|
|||
}
|
||||
Ok((Self::BigInt { bits: b, ty }, rhs))
|
||||
}
|
||||
(
|
||||
lhs @ ComptimeInt::Native { ty: a, .. },
|
||||
rhs @ ComptimeInt::Native { ty: b, .. },
|
||||
) => {
|
||||
(lhs @ ComptimeInt::Native { ty: a, .. }, rhs @ ComptimeInt::Native { ty: b, .. }) => {
|
||||
if a == b {
|
||||
Ok((lhs, rhs))
|
||||
} else {
|
||||
Err(Error::IncompatibleTypes)
|
||||
}
|
||||
}
|
||||
(
|
||||
lhs @ ComptimeInt::BigInt { ty: a, .. },
|
||||
rhs @ ComptimeInt::BigInt { ty: b, .. },
|
||||
) => {
|
||||
(lhs @ ComptimeInt::BigInt { ty: a, .. }, rhs @ ComptimeInt::BigInt { ty: b, .. }) => {
|
||||
if a == b {
|
||||
Ok((lhs, rhs))
|
||||
} else {
|
||||
|
|
@ -2063,56 +1911,36 @@ pub enum ComptimeFloat {
|
|||
impl ComptimeFloat {
|
||||
pub fn add(self, other: Self) -> Result<ComptimeFloat> {
|
||||
match (self, other) {
|
||||
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => {
|
||||
Ok(Self::Binary32(a + b))
|
||||
}
|
||||
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => {
|
||||
Ok(Self::Binary64(a + b))
|
||||
}
|
||||
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => Ok(Self::Binary32(a + b)),
|
||||
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => Ok(Self::Binary64(a + b)),
|
||||
_ => Err(Error::IncompatibleTypes),
|
||||
}
|
||||
}
|
||||
pub fn sub(self, other: Self) -> Result<ComptimeFloat> {
|
||||
match (self, other) {
|
||||
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => {
|
||||
Ok(Self::Binary32(a - b))
|
||||
}
|
||||
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => {
|
||||
Ok(Self::Binary64(a - b))
|
||||
}
|
||||
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => Ok(Self::Binary32(a - b)),
|
||||
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => Ok(Self::Binary64(a - b)),
|
||||
_ => Err(Error::IncompatibleTypes),
|
||||
}
|
||||
}
|
||||
pub fn mul(self, other: Self) -> Result<ComptimeFloat> {
|
||||
match (self, other) {
|
||||
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => {
|
||||
Ok(Self::Binary32(a * b))
|
||||
}
|
||||
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => {
|
||||
Ok(Self::Binary64(a * b))
|
||||
}
|
||||
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => Ok(Self::Binary32(a * b)),
|
||||
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => Ok(Self::Binary64(a * b)),
|
||||
_ => Err(Error::IncompatibleTypes),
|
||||
}
|
||||
}
|
||||
pub fn div(self, other: Self) -> Result<ComptimeFloat> {
|
||||
match (self, other) {
|
||||
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => {
|
||||
Ok(Self::Binary32(a / b))
|
||||
}
|
||||
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => {
|
||||
Ok(Self::Binary64(a / b))
|
||||
}
|
||||
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => Ok(Self::Binary32(a / b)),
|
||||
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => Ok(Self::Binary64(a / b)),
|
||||
_ => Err(Error::IncompatibleTypes),
|
||||
}
|
||||
}
|
||||
pub fn rem(self, other: Self) -> Result<ComptimeFloat> {
|
||||
match (self, other) {
|
||||
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => {
|
||||
Ok(Self::Binary32(a % b))
|
||||
}
|
||||
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => {
|
||||
Ok(Self::Binary64(a % b))
|
||||
}
|
||||
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => Ok(Self::Binary32(a % b)),
|
||||
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => Ok(Self::Binary64(a % b)),
|
||||
_ => Err(Error::IncompatibleTypes),
|
||||
}
|
||||
}
|
||||
|
|
@ -2124,12 +1952,8 @@ impl ComptimeFloat {
|
|||
}
|
||||
pub fn cmp(self, other: Self) -> Result<Ordering> {
|
||||
let ord = match (self, other) {
|
||||
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => {
|
||||
a.partial_cmp(&b)
|
||||
}
|
||||
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => {
|
||||
a.partial_cmp(&b)
|
||||
}
|
||||
(ComptimeFloat::Binary32(a), ComptimeFloat::Binary32(b)) => a.partial_cmp(&b),
|
||||
(ComptimeFloat::Binary64(a), ComptimeFloat::Binary64(b)) => a.partial_cmp(&b),
|
||||
_ => {
|
||||
return Err(Error::IncompatibleTypes);
|
||||
}
|
||||
|
|
@ -2281,9 +2105,7 @@ impl ComptimeNumber {
|
|||
// (ComptimeNumber::Floating(a), ComptimeNumber::Floating(b)) => {
|
||||
// Ok(Self::Floating(a.sub(b)?))
|
||||
// }
|
||||
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => {
|
||||
Ok(Self::Bool(a.bitand(b)))
|
||||
}
|
||||
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => Ok(Self::Bool(a.bitand(b))),
|
||||
_ => Err(Error::IncompatibleTypes),
|
||||
}
|
||||
}
|
||||
|
|
@ -2295,9 +2117,7 @@ impl ComptimeNumber {
|
|||
// (ComptimeNumber::Floating(a), ComptimeNumber::Floating(b)) => {
|
||||
// Ok(Self::Floating(a.bitor(b)?))
|
||||
// }
|
||||
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => {
|
||||
Ok(Self::Bool(a.bitor(b)))
|
||||
}
|
||||
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => Ok(Self::Bool(a.bitor(b))),
|
||||
_ => Err(Error::IncompatibleTypes),
|
||||
}
|
||||
}
|
||||
|
|
@ -2309,9 +2129,7 @@ impl ComptimeNumber {
|
|||
// (ComptimeNumber::Floating(a), ComptimeNumber::Floating(b)) => {
|
||||
// Ok(Self::Floating(a.bitxor(b)?))
|
||||
// }
|
||||
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => {
|
||||
Ok(Self::Bool(a.bitxor(b)))
|
||||
}
|
||||
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => Ok(Self::Bool(a.bitxor(b))),
|
||||
_ => Err(Error::IncompatibleTypes),
|
||||
}
|
||||
}
|
||||
|
|
@ -2347,9 +2165,7 @@ impl ComptimeNumber {
|
|||
// (ComptimeNumber::Floating(a), ComptimeNumber::Floating(b)) => {
|
||||
// Ok(Self::Floating(a.bitxor(b)?))
|
||||
// }
|
||||
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => {
|
||||
Ok(Self::Bool(a || b))
|
||||
}
|
||||
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => Ok(Self::Bool(a || b)),
|
||||
_ => Err(Error::IncompatibleTypes),
|
||||
}
|
||||
}
|
||||
|
|
@ -2361,35 +2177,23 @@ impl ComptimeNumber {
|
|||
// (ComptimeNumber::Floating(a), ComptimeNumber::Floating(b)) => {
|
||||
// Ok(Self::Floating(a.bitxor(b)?))
|
||||
// }
|
||||
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => {
|
||||
Ok(Self::Bool(a && b))
|
||||
}
|
||||
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => Ok(Self::Bool(a && b)),
|
||||
_ => Err(Error::IncompatibleTypes),
|
||||
}
|
||||
}
|
||||
pub fn eq(self, other: Self) -> Result<Self> {
|
||||
match (self, other) {
|
||||
(ComptimeNumber::Integral(a), ComptimeNumber::Integral(b)) => {
|
||||
Ok(Self::Bool(a == b))
|
||||
}
|
||||
(ComptimeNumber::Floating(a), ComptimeNumber::Floating(b)) => {
|
||||
Ok(Self::Bool(a == b))
|
||||
}
|
||||
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => {
|
||||
Ok(Self::Bool(a == b))
|
||||
}
|
||||
(ComptimeNumber::Integral(a), ComptimeNumber::Integral(b)) => Ok(Self::Bool(a == b)),
|
||||
(ComptimeNumber::Floating(a), ComptimeNumber::Floating(b)) => Ok(Self::Bool(a == b)),
|
||||
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => Ok(Self::Bool(a == b)),
|
||||
_ => Err(Error::IncompatibleTypes),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cmp(self, other: Self) -> Result<Ordering> {
|
||||
let ord = match (self, other) {
|
||||
(ComptimeNumber::Integral(a), ComptimeNumber::Integral(b)) => {
|
||||
a.cmp(b)?
|
||||
}
|
||||
(ComptimeNumber::Floating(a), ComptimeNumber::Floating(b)) => {
|
||||
a.cmp(b)?
|
||||
}
|
||||
(ComptimeNumber::Integral(a), ComptimeNumber::Integral(b)) => a.cmp(b)?,
|
||||
(ComptimeNumber::Floating(a), ComptimeNumber::Floating(b)) => a.cmp(b)?,
|
||||
(ComptimeNumber::Bool(a), ComptimeNumber::Bool(b)) => a.cmp(&b),
|
||||
_ => {
|
||||
return Err(Error::IncompatibleTypes);
|
||||
|
|
@ -2429,17 +2233,12 @@ impl ComptimeNumber {
|
|||
match self {
|
||||
ComptimeNumber::Integral(i) => match i {
|
||||
ComptimeInt::Native { bits, .. } => Ok((bits != 0).into()),
|
||||
ComptimeInt::Comptime(bits)
|
||||
| ComptimeInt::BigInt { bits, .. } => {
|
||||
ComptimeInt::Comptime(bits) | ComptimeInt::BigInt { bits, .. } => {
|
||||
Ok((bits.sign() != Sign::NoSign).into())
|
||||
}
|
||||
},
|
||||
ComptimeNumber::Floating(ComptimeFloat::Binary32(f)) => {
|
||||
Ok((f != 0.0).into())
|
||||
}
|
||||
ComptimeNumber::Floating(ComptimeFloat::Binary64(f)) => {
|
||||
Ok((f != 0.0).into())
|
||||
}
|
||||
ComptimeNumber::Floating(ComptimeFloat::Binary32(f)) => Ok((f != 0.0).into()),
|
||||
ComptimeNumber::Floating(ComptimeFloat::Binary64(f)) => Ok((f != 0.0).into()),
|
||||
a => Ok(a),
|
||||
}
|
||||
}
|
||||
|
|
@ -2447,29 +2246,19 @@ impl ComptimeNumber {
|
|||
pub fn into_int(self, ty: IntegralType) -> Result<Self> {
|
||||
match self {
|
||||
ComptimeNumber::Integral(i) => match i {
|
||||
ComptimeInt::Native { bits, .. } => {
|
||||
Ok((bits & ty.u128_bitmask(), ty).into())
|
||||
}
|
||||
ComptimeInt::Comptime(bits)
|
||||
| ComptimeInt::BigInt { bits, .. } => {
|
||||
let max = BigUint::from(2u32)
|
||||
.pow((ty.bits - ty.signed as u16) as u32);
|
||||
ComptimeInt::Native { bits, .. } => Ok((bits & ty.u128_bitmask(), ty).into()),
|
||||
ComptimeInt::Comptime(bits) | ComptimeInt::BigInt { bits, .. } => {
|
||||
let max = BigUint::from(2u32).pow((ty.bits - ty.signed as u16) as u32);
|
||||
let (sign, data) = bits.into_parts();
|
||||
let data = data.clamp(BigUint::ZERO, max);
|
||||
|
||||
Ok((BigInt::from_biguint(sign, data), ty).into())
|
||||
}
|
||||
},
|
||||
ComptimeNumber::Bool(b) => {
|
||||
Ok((b as u128 & ty.u128_bitmask(), ty).into())
|
||||
}
|
||||
ComptimeNumber::Bool(b) => Ok((b as u128 & ty.u128_bitmask(), ty).into()),
|
||||
ComptimeNumber::Floating(f) => match f {
|
||||
ComptimeFloat::Binary32(f) => {
|
||||
Ok((f as u128 & ty.u128_bitmask(), ty).into())
|
||||
}
|
||||
ComptimeFloat::Binary64(f) => {
|
||||
Ok((f as u128 & ty.u128_bitmask(), ty).into())
|
||||
}
|
||||
ComptimeFloat::Binary32(f) => Ok((f as u128 & ty.u128_bitmask(), ty).into()),
|
||||
ComptimeFloat::Binary64(f) => Ok((f as u128 & ty.u128_bitmask(), ty).into()),
|
||||
},
|
||||
ComptimeNumber::Void => {
|
||||
return Err(Error::VoidConversion);
|
||||
|
|
@ -2480,8 +2269,7 @@ impl ComptimeNumber {
|
|||
let f = match self {
|
||||
ComptimeNumber::Integral(i) => match i {
|
||||
ComptimeInt::Native { bits, .. } => bits as f64,
|
||||
ComptimeInt::Comptime(bits)
|
||||
| ComptimeInt::BigInt { bits, .. } => {
|
||||
ComptimeInt::Comptime(bits) | ComptimeInt::BigInt { bits, .. } => {
|
||||
bits.to_f64().unwrap_or(f64::NAN)
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
#![feature(
|
||||
extract_if,
|
||||
iter_advance_by,
|
||||
box_into_inner,
|
||||
hash_extract_if,
|
||||
bigint_helper_methods,
|
||||
map_try_insert,
|
||||
iter_intersperse,
|
||||
|
|
@ -10,9 +8,10 @@
|
|||
int_roundings,
|
||||
if_let_guard,
|
||||
debug_closure_helpers,
|
||||
box_vec_non_null,
|
||||
macro_metavar_expr
|
||||
)]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused_macros, unsafe_op_in_unsafe_fn)]
|
||||
|
||||
pub mod asm;
|
||||
pub mod ast;
|
||||
|
|
@ -29,6 +28,9 @@ pub mod symbol_table;
|
|||
pub mod tokens;
|
||||
pub mod triples;
|
||||
|
||||
pub mod utils;
|
||||
use utils::unit;
|
||||
|
||||
pub fn tokenize<'a>(
|
||||
bytes: &'a [u8],
|
||||
) -> Result<lexer::Tokenizer<'a>, (lexer::Tokenizer<'a>, Vec<lexer::TokenizeError>)> {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use itertools::Itertools;
|
|||
use num_bigint::{BigInt, BigUint};
|
||||
|
||||
use crate::{
|
||||
BitSize,
|
||||
ast::{self, FloatingType, IntegralType, LetOrVar, Node, Tag, Type},
|
||||
ast2::intern::{self, AMD64_POINTER_BITS},
|
||||
common::NextIf,
|
||||
|
|
@ -9,8 +10,7 @@ use crate::{
|
|||
error::{AnalysisError, AnalysisErrorTag},
|
||||
lexer::{Radix, TokenIterator},
|
||||
symbol_table::{SymbolKind, SymbolTable},
|
||||
tokens::{Token, PRECEDENCE_MAP},
|
||||
BitSize,
|
||||
tokens::{PRECEDENCE_MAP, Token},
|
||||
};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
|
|
@ -1767,10 +1767,10 @@ impl Tree {
|
|||
}
|
||||
|
||||
pub fn peer_type_of_nodes_unwrap(&self, lhs: Node, rhs: Node) -> Type {
|
||||
self.peer_type_of_nodes(lhs, rhs).expect({
|
||||
self.peer_type_of_nodes(lhs, rhs).expect(&{
|
||||
let at = self.type_of_node(lhs);
|
||||
let bt = self.type_of_node(rhs);
|
||||
&format!("incompatible types for %{lhs}({at}) and %{rhs}({bt})")
|
||||
format!("incompatible types for %{lhs}({at}) and %{rhs}({bt})")
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -1844,10 +1844,10 @@ impl Tree {
|
|||
} => {
|
||||
let ty = match (explicit_type.as_ref(), assignment) {
|
||||
(None, b) => self.type_of_node(*b),
|
||||
(Some(a), b) => self.peer_type_of_nodes(*a, *b).expect({
|
||||
(Some(a), b) => self.peer_type_of_nodes(*a, *b).expect(&{
|
||||
let at = self.type_of_node(*a);
|
||||
let bt = self.type_of_node(*b);
|
||||
&format!("incompatible types for %{a}({at}) and %{b}({bt})")
|
||||
format!("incompatible types for %{a}({at}) and %{b}({bt})")
|
||||
}),
|
||||
};
|
||||
|
||||
|
|
@ -1863,10 +1863,10 @@ impl Tree {
|
|||
(None, None) => panic!("%{node}: no type specified?"),
|
||||
(None, Some(b)) => self.type_of_node(*b),
|
||||
(Some(a), None) => self.type_of_node(*a),
|
||||
(Some(a), Some(b)) => self.peer_type_of_nodes(*a, *b).expect({
|
||||
(Some(a), Some(b)) => self.peer_type_of_nodes(*a, *b).expect(&{
|
||||
let at = self.type_of_node(*a);
|
||||
let bt = self.type_of_node(*b);
|
||||
&format!("incompatible types for %{a}({at}) and %{b}({bt})")
|
||||
format!("incompatible types for %{a}({at}) and %{b}({bt})")
|
||||
}),
|
||||
};
|
||||
|
||||
|
|
@ -1888,10 +1888,10 @@ impl Tree {
|
|||
| Tag::And { lhs, rhs }
|
||||
| Tag::BitOr { lhs, rhs }
|
||||
| Tag::BitAnd { lhs, rhs }
|
||||
| Tag::BitXOr { lhs, rhs } => self.peer_type_of_nodes(*lhs, *rhs).expect({
|
||||
| Tag::BitXOr { lhs, rhs } => self.peer_type_of_nodes(*lhs, *rhs).expect(&{
|
||||
let at = self.type_of_node(*lhs);
|
||||
let bt = self.type_of_node(*rhs);
|
||||
&format!("incompatible types for %{lhs}({at}) and %{rhs}({bt})")
|
||||
format!("incompatible types for %{lhs}({at}) and %{rhs}({bt})")
|
||||
}),
|
||||
Tag::Shl { lhs, .. } => self.type_of_node(*lhs),
|
||||
Tag::Shr { lhs, .. } => self.type_of_node(*lhs),
|
||||
|
|
@ -1906,11 +1906,11 @@ impl Tree {
|
|||
Tag::IfExpr { .. } => Type::void(),
|
||||
Tag::IfElseExpr {
|
||||
body, else_expr, ..
|
||||
} => self.peer_type_of_nodes(*body, *else_expr).expect({
|
||||
} => self.peer_type_of_nodes(*body, *else_expr).expect(&{
|
||||
let (lhs, rhs) = (body, else_expr);
|
||||
let at = self.type_of_node(*lhs);
|
||||
let bt = self.type_of_node(*rhs);
|
||||
&format!("incompatible types for %{lhs}({at}) and %{rhs}({bt})")
|
||||
format!("incompatible types for %{lhs}({at}) and %{rhs}({bt})")
|
||||
}),
|
||||
_ => Type::void(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,25 +35,13 @@ impl SymbolPath {
|
|||
for node in self.0.iter().skip(1).rev() {
|
||||
match tree.nodes.get_node(node.unwrap()) {
|
||||
Tag::VarDecl { name, .. } => {
|
||||
_ = write!(
|
||||
&mut buf,
|
||||
"V{}::",
|
||||
tree.get_ident_str(*name).unwrap()
|
||||
);
|
||||
_ = write!(&mut buf, "V{}::", tree.get_ident_str(*name).unwrap());
|
||||
}
|
||||
Tag::GlobalDecl { name, .. } => {
|
||||
_ = write!(
|
||||
&mut buf,
|
||||
"G{}::",
|
||||
tree.get_ident_str(*name).unwrap()
|
||||
);
|
||||
_ = write!(&mut buf, "G{}::", tree.get_ident_str(*name).unwrap());
|
||||
}
|
||||
Tag::FunctionProto { name, .. } => {
|
||||
_ = write!(
|
||||
&mut buf,
|
||||
"F{}::",
|
||||
tree.get_ident_str(*name).unwrap()
|
||||
);
|
||||
_ = write!(&mut buf, "F{}::", tree.get_ident_str(*name).unwrap());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -81,11 +69,11 @@ impl InnerSymbolTable {
|
|||
Self::new_with(Self::new_inner)
|
||||
}
|
||||
|
||||
fn new_with<G>(gen: G) -> NonNull<InnerSymbolTable>
|
||||
fn new_with<G>(r#gen: G) -> NonNull<InnerSymbolTable>
|
||||
where
|
||||
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 {
|
||||
|
|
@ -142,12 +130,7 @@ impl Drop for InnerSymbolTable {
|
|||
}
|
||||
|
||||
impl InnerSymbolTable {
|
||||
fn insert_symbol(
|
||||
&mut self,
|
||||
name: &str,
|
||||
node: AstNode,
|
||||
kind: SymbolKind,
|
||||
) -> &SymbolRecord {
|
||||
fn insert_symbol(&mut self, name: &str, node: AstNode, kind: SymbolKind) -> &SymbolRecord {
|
||||
match kind {
|
||||
SymbolKind::Var => {
|
||||
self.ordered_identifiers.push(SymbolRecord {
|
||||
|
|
@ -160,11 +143,7 @@ impl InnerSymbolTable {
|
|||
}
|
||||
}
|
||||
|
||||
fn insert_orderless_symbol(
|
||||
&mut self,
|
||||
name: &str,
|
||||
node: AstNode,
|
||||
) -> &SymbolRecord {
|
||||
fn insert_orderless_symbol(&mut self, name: &str, node: AstNode) -> &SymbolRecord {
|
||||
self.orderless_identifiers.insert(
|
||||
name.to_owned(),
|
||||
SymbolRecord {
|
||||
|
|
@ -175,11 +154,7 @@ impl InnerSymbolTable {
|
|||
self.orderless_identifiers.get(name).unwrap()
|
||||
}
|
||||
|
||||
fn find_symbol_or_insert_with<'a, F>(
|
||||
&'a mut self,
|
||||
name: &str,
|
||||
cb: F,
|
||||
) -> &'a SymbolRecord
|
||||
fn find_symbol_or_insert_with<'a, F>(&'a mut self, name: &str, cb: F) -> &'a SymbolRecord
|
||||
where
|
||||
F: FnOnce() -> (AstNode, SymbolKind),
|
||||
{
|
||||
|
|
@ -202,9 +177,7 @@ impl InnerSymbolTable {
|
|||
.find(|(_, v)| v.decl == decl)
|
||||
.map(|(_, v)| v)
|
||||
})
|
||||
.or_else(|| {
|
||||
self.parent_ref().and_then(|p| p.find_symbol_by_decl(decl))
|
||||
})
|
||||
.or_else(|| self.parent_ref().and_then(|p| p.find_symbol_by_decl(decl)))
|
||||
}
|
||||
|
||||
fn find_any_symbol(&self, name: &str) -> Option<&SymbolRecord> {
|
||||
|
|
@ -219,9 +192,7 @@ impl InnerSymbolTable {
|
|||
self.ordered_identifiers
|
||||
.iter()
|
||||
.find(|r| r.name.as_str() == name)
|
||||
.or_else(|| {
|
||||
self.parent_ref().and_then(|p| p.find_ordered_symbol(name))
|
||||
})
|
||||
.or_else(|| self.parent_ref().and_then(|p| p.find_ordered_symbol(name)))
|
||||
}
|
||||
|
||||
fn find_orderless_symbol(&self, name: &str) -> Option<&SymbolRecord> {
|
||||
|
|
@ -315,12 +286,7 @@ impl SymbolTableWrapper {
|
|||
}
|
||||
|
||||
impl SymbolTableWrapper {
|
||||
pub fn insert_symbol(
|
||||
&mut self,
|
||||
name: &str,
|
||||
node: AstNode,
|
||||
kind: SymbolKind,
|
||||
) -> &SymbolRecord {
|
||||
pub fn insert_symbol(&mut self, name: &str, node: AstNode, kind: SymbolKind) -> &SymbolRecord {
|
||||
self.current_mut().insert_symbol(name, node, kind)
|
||||
}
|
||||
|
||||
|
|
@ -328,27 +294,15 @@ impl SymbolTableWrapper {
|
|||
self.root_mut().find_orderless_symbol(name)
|
||||
}
|
||||
|
||||
pub fn insert_root_symbol(
|
||||
&mut self,
|
||||
name: &str,
|
||||
node: AstNode,
|
||||
) -> &SymbolRecord {
|
||||
pub fn insert_root_symbol(&mut self, name: &str, node: AstNode) -> &SymbolRecord {
|
||||
self.root_mut().insert_orderless_symbol(name, node)
|
||||
}
|
||||
|
||||
pub fn insert_orderless_symbol(
|
||||
&mut self,
|
||||
name: &str,
|
||||
node: AstNode,
|
||||
) -> &SymbolRecord {
|
||||
pub fn insert_orderless_symbol(&mut self, name: &str, node: AstNode) -> &SymbolRecord {
|
||||
self.current_mut().insert_orderless_symbol(name, node)
|
||||
}
|
||||
|
||||
pub fn find_symbol_or_insert_with<'a, F>(
|
||||
&'a mut self,
|
||||
name: &str,
|
||||
cb: F,
|
||||
) -> &'a SymbolRecord
|
||||
pub fn find_symbol_or_insert_with<'a, F>(&'a mut self, name: &str, cb: F) -> &'a SymbolRecord
|
||||
where
|
||||
F: FnOnce() -> (AstNode, SymbolKind),
|
||||
{
|
||||
|
|
@ -450,8 +404,8 @@ pub mod syms2 {
|
|||
use std::collections::BTreeMap;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::ast2::intern::Index as InternIndex;
|
||||
use crate::ast2::Index as AstIndex;
|
||||
use crate::ast2::intern::Index as InternIndex;
|
||||
use crate::lexer::SourceLocation;
|
||||
|
||||
#[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)]
|
||||
pub enum SymbolKind {
|
||||
__First,
|
||||
|
|
@ -479,6 +467,7 @@ pub mod syms2 {
|
|||
__TypeScope,
|
||||
Scope,
|
||||
ParentScope,
|
||||
Parameter(SourceLocation),
|
||||
Local(SourceLocation),
|
||||
__Last,
|
||||
}
|
||||
|
|
@ -526,9 +515,7 @@ pub mod syms2 {
|
|||
}
|
||||
let entries = self.inner.iter().map(|(key, val)| {
|
||||
let payload = match key {
|
||||
Key::ScopeByIndex { .. } => {
|
||||
ExpandedPayload::Intern(val.as_intern())
|
||||
}
|
||||
Key::ScopeByIndex { .. } => ExpandedPayload::Intern(val.as_intern()),
|
||||
_ => ExpandedPayload::Ast(val.as_ast()),
|
||||
};
|
||||
|
||||
|
|
@ -567,7 +554,7 @@ pub mod syms2 {
|
|||
scope: AstIndex,
|
||||
name: InternIndex,
|
||||
loc: SourceLocation,
|
||||
) -> Option<AstIndex> {
|
||||
) -> Option<(Key, AstIndex)> {
|
||||
use SymbolKind::*;
|
||||
let range = self.inner.range(
|
||||
Key::Symbol {
|
||||
|
|
@ -581,8 +568,8 @@ pub mod syms2 {
|
|||
},
|
||||
);
|
||||
|
||||
if let Some((_, payload)) = range.rev().next() {
|
||||
Some(payload.as_ast())
|
||||
if let Some((key, payload)) = range.rev().next() {
|
||||
Some((*key, payload.as_ast()))
|
||||
} else {
|
||||
if let Some(parent) = self.inner.get(&Key::Symbol {
|
||||
scope,
|
||||
|
|
@ -601,7 +588,7 @@ pub mod syms2 {
|
|||
scope: AstIndex,
|
||||
name: InternIndex,
|
||||
loc: SourceLocation,
|
||||
) -> Option<AstIndex> {
|
||||
) -> Option<(Key, AstIndex)> {
|
||||
use SymbolKind::*;
|
||||
let range = self.inner.range(
|
||||
Key::Symbol {
|
||||
|
|
@ -615,15 +602,15 @@ pub mod syms2 {
|
|||
},
|
||||
);
|
||||
|
||||
if let Some((_, payload)) = range.rev().next() {
|
||||
Some(payload.as_ast())
|
||||
if let Some((key, payload)) = range.rev().next() {
|
||||
Some((*key, payload.as_ast()))
|
||||
} else {
|
||||
if let Some(parent) = self.inner.get(&Key::Symbol {
|
||||
scope,
|
||||
name: InternIndex::invalid(),
|
||||
kind: ParentScope,
|
||||
}) {
|
||||
self.find_symbol(parent.as_ast(), name, loc)
|
||||
self.find_type_symbol(parent.as_ast(), name, loc)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -637,10 +624,8 @@ pub mod syms2 {
|
|||
kind: SymbolKind,
|
||||
ast: AstIndex,
|
||||
) {
|
||||
self.inner.insert(
|
||||
Key::Symbol { scope, name, kind },
|
||||
Payload::new_ast(ast),
|
||||
);
|
||||
self.inner
|
||||
.insert(Key::Symbol { scope, name, kind }, Payload::new_ast(ast));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use std::{
|
|||
|
||||
use crate::{
|
||||
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,
|
||||
variant, write_indented, writeln_indented,
|
||||
};
|
||||
|
|
@ -159,10 +159,13 @@ pub enum Inst {
|
|||
Constant,
|
||||
/// size, align
|
||||
Alloca,
|
||||
/// (pointee type)
|
||||
/// src
|
||||
Load(intern::Index),
|
||||
/// (pointee type)
|
||||
/// src, dst
|
||||
Store(intern::Index),
|
||||
/// (pointer type)
|
||||
/// ptr, index,
|
||||
GetElementPtr(intern::Index),
|
||||
/// size, align
|
||||
|
|
@ -605,10 +608,10 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> {
|
|||
let ty = self
|
||||
.tree
|
||||
.peer_type_of_nodes(*lhs, *rhs)
|
||||
.expect({
|
||||
.expect(&{
|
||||
let at = self.tree.type_of_node(*lhs);
|
||||
let bt = self.tree.type_of_node(*rhs);
|
||||
&format!("incompatible types for %{lhs}({at}) and %{rhs}({bt})")
|
||||
format!("incompatible types for %{lhs}({at}) and %{rhs}({bt})")
|
||||
})
|
||||
.into();
|
||||
|
||||
|
|
@ -759,8 +762,8 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> {
|
|||
}
|
||||
|
||||
pub struct IR {
|
||||
nodes: Vec<Inst>,
|
||||
data: Vec<Option<Data>>,
|
||||
pub(crate) nodes: Vec<Inst>,
|
||||
pub(crate) data: Vec<Option<Data>>,
|
||||
// intern_pool: &'a mut InternPool,
|
||||
}
|
||||
|
||||
|
|
|
|||
25
src/utils.rs
Normal file
25
src/utils.rs
Normal 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())();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue