SeaLang/src/ast2/mod.rs
2025-03-06 01:12:25 +01:00

5828 lines
202 KiB
Rust

#![allow(dead_code)]
use std::{
collections::BTreeMap,
fmt::{Debug, Display},
};
use intern::{InternPool, PointerFlags, StructFlags, AMD64_POINTER_TYPE_INFO};
use num_bigint::BigInt;
use crate::{
ast::FloatingType, comptime::ComptimeNumber, lexer::SourceLocation, tokens::Token,
writeln_indented,
};
pub mod intern;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Tag {
/// pseudo tag, contains a range from a..b into extra of all files.
Root,
/// `data` is a range from a..b into extra of all global nodes.
File,
/// `data` is an intern to a name, and an index into extra of [index: return_type, index: ParameterList]
FunctionProto,
/// `data` is an index to a FunctionProto and an index to a Block
FunctionDecl,
/// `data` is a range from a..b into extra of indices to parameters
ParameterList,
/// `data` is an index to a type, and an intern to a name
Parameter,
/// `data` is range from a..b into `extra` of indices to statements
Block,
/// `data` is range from a..b into `extra` of indices to statements, where the last one is an expression
BlockTrailingExpr,
/// `data` is an index to a type, and an intern to a value
Constant,
/// `data` is an index to an expression
ExprStmt,
/// `data` is none
ReturnStmt,
/// `data` is an index to an expr
ReturnExprStmt,
/// `data` is a range from a..b into `extra` of `[name: intern, type: index]`
VarDecl,
/// `data` is a range from a..b into `extra` of `[name: intern, type: index]`
MutVarDecl,
/// `data` is a range from a..b into `extra` of `[name: intern, expr: index, type?: index]`
VarDeclAssignment,
/// `data` is a range from a..b into `extra` of `[name: intern, expr: index, type?: index]`
MutVarDeclAssignment,
/// `data` is an intern to a name, and an offset into `extra` of `[type: index, expr: index]`
GlobalDecl,
/// `data` is an intern to a name, and an offset into extra of `[flags, type0 ,..., typeN ,name0 ,..., nameN]`
StructDecl,
/// `data` is an index to a type, and an intern to a name
FieldDecl,
/// `data` is an index to a VarDecl, GlobalDecl or FunctionDecl
DeclRef,
/// `data` is an inlined key into the symbol table (scope: index, name: intern)
DeclRefUnresolved,
/// `data` is an intern of a type
InternedType,
/// `data` is an index to a StructDecl
TypeDeclRef,
/// `data` is an inlined key into the symbol table (scope: index, name: intern)
TypeDeclRefUnresolved,
/// `data` is an index to a Type and u32 PointerFlags (extra offset)
PointerType,
/// `data` is an index to a length expression, and an underlying pointer type
ArrayType,
/// `data` is an index to an expr and an index to an ArgumentList
CallExpr,
/// `data` is an index to an expr and an intern to a field name
FieldAccess,
/// `data` is a range from a..b into extra of indices to arguments
ArgumentList,
/// `data` is an index to an expression
Argument,
/// `data` is an index to an expression, and an intern to a name
NamedArgument,
/// `data` is an index to lhs, and an index to the type
ExplicitCast,
/// `data` is a single index to an expr
Deref,
AddressOf,
Not,
Negate,
PlaceToValueConversion,
ValueToPlaceConversion,
/// data is two indices for `lhs` and `rhs`
Or,
And,
BitOr,
BitXOr,
BitAnd,
Eq,
NEq,
Lt,
Gt,
Le,
Ge,
Shl,
Shr,
Add,
Sub,
Mul,
Div,
Rem,
Assign,
SubscriptExpr,
IfExpr,
/// `data` is an index to an expression and an index into extra for [if, else]
IfElseExpr,
// TODO:
/// `data` is a ParseError
Error,
/// placeholder tag for reserved indices/nodes, `data` is none
Undefined,
}
impl Tag {
fn is_type(&self) -> bool {
match self {
Tag::TypeDeclRef
| Tag::TypeDeclRefUnresolved
| Tag::PointerType
| Tag::InternedType
| Tag::ArrayType
| Tag::StructDecl => true,
_ => false,
}
}
fn is_expr(&self) -> bool {
match self {
Tag::Constant
| Tag::DeclRef
| Tag::Deref
| Tag::CallExpr
| Tag::AddressOf
| Tag::Not
| Tag::Negate
| 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
| Tag::SubscriptExpr
| Tag::IfExpr
| Tag::IfElseExpr
| Tag::Block
| &Tag::BlockTrailingExpr => true,
_ => false,
}
}
}
#[derive(Debug, Clone, Copy, thiserror::Error, PartialEq, Eq)]
enum ParseError {
#[error("Unexpected end of token iter.")]
UnexpectedEndOfTokens,
#[error("Expected Token {0}.")]
ExpectedToken(Token),
#[error("Expected Token {0}, but other token was found.")]
ExpectedTokenNotFound(Token),
#[error("Expected either a function declaration or a global variable.")]
UnexpectedTokenAtFileScope,
#[error("Expected Ident.")]
ExpectedIdent,
#[error("Integral types may not be wider than 65535 bits.")]
IntegralTypeTooWide,
#[error("Expected typename.")]
ExpectedTypeName,
#[error("Dummy Message.")]
ExpectedFunctionPrototype,
#[error("Dummy Message.")]
ExpectedPrimaryExpression,
#[error("Dummy Message.")]
ExpectedConstantLiteral,
#[error("Dummy Message.")]
ExpectedExpression,
#[error("Dummy Message.")]
ExpectedPostfixExpression,
#[error("Dummy Message.")]
ExpectedPrefixExpression,
#[error("Dummy Message.")]
ExpectedArgumentList,
#[error("Dummy Message.")]
ExpectedStatement,
#[error("Dummy Message.")]
UnmatchedParens(u32),
#[error("Dummy Message.")]
ExpectedTypeDeclaration,
#[error("Dummy Message.")]
UnexpectedTypeAttributes,
#[error("Dummy Message.")]
UnmatchedSquareBracket(u32),
#[error("Dummy Message.")]
ExpectedEndOfBlock,
#[error("Dummy Message.")]
UnmatchedBrace(u32),
#[error("Dummy Message.")]
UnmatchedDelimiter(u32),
#[error("Error in child node {0:?}.")]
ErrorNode(Index),
}
#[repr(packed)]
#[derive(Clone, Copy)]
struct Node {
/// defines the type of the node in the tree
tag: Tag,
data: Data,
}
#[derive(Clone, Copy)]
pub union Data {
none: (),
error: ParseError,
index: Index,
two_indices: (Index, Index),
range: (Index, Index),
extra_range: (u32, u32),
intern: intern::Index,
index_intern: (Index, intern::Index),
two_interns: (intern::Index, intern::Index),
intern_and_extra_offset: (intern::Index, u32),
index_and_extra_offset: (Index, u32),
index_and_opaque: (Index, u32),
}
impl Default for Tag {
fn default() -> Self {
Tag::Undefined
}
}
impl Default for SourceLocation {
fn default() -> Self {
Self::invalid()
}
}
impl Default for Data {
fn default() -> Self {
Self::none()
}
}
#[derive(Debug)]
#[allow(dead_code)]
enum ExpandedData {
None,
Error(ParseError),
Index(Index),
TwoIndices(Index, Index),
Range(Index, Index),
ExtraRange(usize, usize),
Intern(intern::Index),
IndexIntern(Index, intern::Index),
TwoInterns(intern::Index, intern::Index),
InternAndExtraOffset(intern::Index, usize),
IndexAndExtraOffset(Index, usize),
IndexAndOpaque(Index, u32),
}
impl ExpandedData {
fn from_none() -> Self {
Self::None
}
fn from_error(data: Data) -> Self {
Self::Error(data.as_error())
}
fn from_index(data: Data) -> Self {
Self::Index(data.as_index())
}
fn from_two_indices(data: Data) -> Self {
let data = data.as_two_indices();
Self::TwoIndices(data.0, data.1)
}
fn from_range(data: Data) -> Self {
let data = data.as_index_range();
Self::Range(data.0, data.1)
}
fn from_extra_range(data: Data) -> Self {
let data = data.as_extra_range();
Self::ExtraRange(data.0, data.1)
}
fn from_intern(data: Data) -> Self {
let data = data.as_intern();
Self::Intern(data)
}
fn from_index_intern(data: Data) -> Self {
let data = data.as_index_intern();
Self::IndexIntern(data.0, data.1)
}
fn from_two_interns(data: Data) -> Self {
let data = data.as_two_interns();
Self::TwoInterns(data.0, data.1)
}
fn from_intern_and_extra_offset(data: Data) -> Self {
let data = data.as_intern_and_extra_offset();
Self::InternAndExtraOffset(data.0, data.1)
}
fn from_index_and_extra_offset(data: Data) -> Self {
let data = data.as_index_and_extra_offset();
Self::IndexAndExtraOffset(data.0, data.1)
}
fn from_index_and_opaque(data: Data) -> Self {
let data = data.as_index_and_opaque();
Self::IndexAndExtraOffset(data.0, data.1)
}
}
impl From<(Tag, Data)> for ExpandedData {
fn from((tag, data): (Tag, Data)) -> Self {
match tag {
Tag::FunctionProto => Self::from_index_and_extra_offset(data),
Tag::ParameterList => Self::from_extra_range(data),
Tag::Root => Self::from_extra_range(data),
Tag::File => Self::from_extra_range(data),
Tag::ArgumentList
| Tag::VarDecl
| Tag::MutVarDecl
| Tag::VarDeclAssignment
| Tag::MutVarDeclAssignment
| Tag::BlockTrailingExpr
| Tag::Block => Self::from_extra_range(data),
Tag::FieldDecl | Tag::Constant | Tag::Parameter => Self::from_index_intern(data),
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
| Tag::Assign
| Tag::IfExpr
| Tag::SubscriptExpr
| Tag::CallExpr
| Tag::ArrayType
| Tag::FunctionDecl => Self::from_two_indices(data),
Tag::ReturnExprStmt
| Tag::DeclRef
| Tag::TypeDeclRef
| Tag::Argument
| Tag::Deref
| Tag::AddressOf
| Tag::Not
| Tag::Negate
| Tag::PlaceToValueConversion
| Tag::ValueToPlaceConversion
| Tag::ExprStmt => Self::from_index(data),
Tag::FieldAccess
| Tag::DeclRefUnresolved
| Tag::TypeDeclRefUnresolved
| Tag::NamedArgument
| Tag::ExplicitCast => Self::from_index_intern(data),
Tag::GlobalDecl => Self::from_intern_and_extra_offset(data),
Tag::InternedType | Tag::StructDecl => Self::from_intern(data),
Tag::PointerType | Tag::IfElseExpr => Self::from_index_and_extra_offset(data),
Tag::Error => Self::from_error(data),
Tag::ReturnStmt | Tag::Undefined => Self::from_none(),
}
}
}
impl Data {
fn as_error(self) -> ParseError {
unsafe { self.error }
}
fn as_index(self) -> Index {
unsafe { self.index }
}
fn as_two_indices(self) -> (Index, Index) {
unsafe { self.two_indices }
}
fn as_index_range(self) -> (Index, Index) {
unsafe { self.range }
}
fn as_extra_range(self) -> (usize, usize) {
let (a, b) = unsafe { self.extra_range };
(a as usize, b as usize)
}
fn as_intern(self) -> intern::Index {
unsafe { self.intern }
}
fn as_two_interns(self) -> (intern::Index, intern::Index) {
unsafe { self.two_interns }
}
fn as_index_intern(self) -> (Index, intern::Index) {
unsafe { self.index_intern }
}
fn as_index_and_extra_offset(self) -> (Index, usize) {
let (i, e) = unsafe { self.index_and_extra_offset };
(i, e as usize)
}
fn as_intern_and_extra_offset(self) -> (intern::Index, usize) {
let (i, e) = unsafe { self.intern_and_extra_offset };
(i, e as usize)
}
fn as_index_and_opaque(self) -> (Index, usize) {
let (i, e) = unsafe { self.index_and_opaque };
(i, e as usize)
}
}
impl Data {
fn none() -> Self {
Self { none: () }
}
fn error(error: ParseError) -> Self {
Self { error }
}
fn index(index: Index) -> Self {
Self { index }
}
fn two_indices(a: Index, b: Index) -> Self {
Self {
two_indices: (a, b),
}
}
fn two_interns(a: intern::Index, b: intern::Index) -> Self {
Self {
two_interns: (a, b),
}
}
fn range_of_indices(a: Index, b: Index) -> Self {
Self { range: (a, b) }
}
fn extra_range(a: u32, b: u32) -> Self {
Self {
extra_range: (a, b),
}
}
fn intern(intern: intern::Index) -> Self {
Self { intern }
}
fn index_and_intern(index: Index, intern: intern::Index) -> Self {
Self {
index_intern: (index, intern),
}
}
fn intern_and_extra_offset(intern: intern::Index, offset: u32) -> Self {
Self {
intern_and_extra_offset: (intern, offset),
}
}
fn index_and_extra_offset(index: Index, offset: u32) -> Self {
Self {
index_and_extra_offset: (index, offset),
}
}
fn index_and_opaque(index: Index, value: u32) -> Self {
Self {
index_and_opaque: (index, value),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
enum PlaceOrValue {
Value(index::Index),
Place(index::Index),
}
impl PlaceOrValue {
fn into_index(self) -> index::Index {
match self {
PlaceOrValue::Value(index) => index,
PlaceOrValue::Place(index) => index,
}
}
fn is_value(&self) -> bool {
matches!(self, &Self::Value(_))
}
fn is_place(&self) -> bool {
!self.is_value()
}
fn into_value(self) -> PlaceOrValue {
Self::Value(self.into_index())
}
fn into_place(self) -> PlaceOrValue {
Self::Place(self.into_index())
}
fn eq_discriminant(&self, other: &Self) -> bool {
core::mem::discriminant(self).eq(&core::mem::discriminant(other))
}
fn with_index(self, index: Index) -> PlaceOrValue {
match self {
PlaceOrValue::Value(_) => Self::Value(index),
PlaceOrValue::Place(_) => Self::Place(index),
}
}
}
impl From<index::Index> for PlaceOrValue {
fn from(value: index::Index) -> Self {
Self::Value(value)
}
}
mod index {
use std::num::NonZero;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(u8)]
pub enum Kind {
Other = 0,
Function,
GlobalDecl,
Error,
File,
}
impl TryFrom<u8> for Kind {
type Error = ();
fn try_from(value: u8) -> Result<Self, ()> {
match value {
0 => Ok(Self::Other),
1 => Ok(Self::Function),
2 => Ok(Self::GlobalDecl),
3 => Ok(Self::Error),
4 => Ok(Self::File),
_ => Err(()),
}
}
}
/// Index type that has 28 bits of index and 4 bits of kind.
#[repr(transparent)]
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Index(NonZero<u32>);
impl core::fmt::Debug for Index {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Index")
.field("kind", &self.kind())
.field("index", &self.index_u32())
.finish()
}
}
impl core::fmt::Display for Index {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let label = match self.kind() {
Kind::Other => "",
Kind::Function => "funcs.",
Kind::GlobalDecl => "gdecls.",
Kind::Error => "errors.",
Kind::File => "files.",
};
write!(f, "%{label}{}", self.index_u32())
}
}
impl Index {
pub fn new(kind: Kind, idx: u32) -> Option<Self> {
let inner = NonZero::new((kind as u32) << 28 | idx & ((1 << 28) - 1));
inner.map(Self)
}
pub fn kind(&self) -> Kind {
Kind::try_from(((self.0.get() >> 28) & 0b1111) as u8).unwrap()
}
pub fn index_u32(&self) -> u32 {
self.0.get() & ((1 << 28) - 1)
}
pub fn index_usize(&self) -> usize {
self.index_u32() as usize
}
pub fn from_u32(inner: u32) -> Option<Self> {
NonZero::new(inner).map(Self)
}
pub unsafe fn from_u32_unchecked(inner: u32) -> Self {
Self(NonZero::new_unchecked(inner))
}
pub fn as_u32(self) -> u32 {
self.0.get()
}
pub fn into_u32(self) -> u32 {
self.0.get()
}
}
}
pub use index::{Index, Kind};
#[derive(Debug, Clone)]
struct AstTables<T> {
functions: Vec<T>,
global_decls: Vec<T>,
errors: Vec<T>,
files: Vec<T>,
other: Vec<T>,
}
impl<T> core::ops::Index<index::Index> for AstTables<T> {
type Output = T;
fn index(&self, index: index::Index) -> &Self::Output {
&self.table_ref(index.kind())[index.index_usize()]
}
}
impl<T> core::ops::IndexMut<index::Index> for AstTables<T> {
fn index_mut(&mut self, index: index::Index) -> &mut Self::Output {
&mut self.table_mut(index.kind())[index.index_usize()]
}
}
impl<T> AstTables<T> {
fn new() -> Self {
Self {
functions: Vec::new(),
global_decls: Vec::new(),
errors: Vec::new(),
files: Vec::new(),
other: Vec::new(),
}
}
fn get(&self, index: index::Index) -> Option<&T> {
self.table_ref(index.kind()).get(index.index_usize())
}
unsafe fn get_unchecked(&self, index: index::Index) -> &T {
self.table_ref(index.kind())
.get_unchecked(index.index_usize())
}
fn init() -> Self
where
T: Default,
{
Self {
functions: Vec::new(),
global_decls: Vec::new(),
errors: Vec::new(),
files: Vec::new(),
other: vec![T::default()],
}
}
fn push(&mut self, kind: index::Kind, t: T) -> index::Index {
let table = self.table_mut(kind);
let i = table.len();
table.push(t);
index::Index::new(kind, i as u32).unwrap()
}
fn reserve(&mut self, kind: index::Kind) -> index::Index
where
T: Default,
{
self.push(kind, T::default())
}
fn table_mut(&mut self, kind: index::Kind) -> &mut Vec<T> {
match kind {
index::Kind::Other => &mut self.other,
index::Kind::Function => &mut self.functions,
index::Kind::GlobalDecl => &mut self.global_decls,
index::Kind::Error => &mut self.errors,
index::Kind::File => &mut self.files,
}
}
fn table_ref(&self, kind: index::Kind) -> &Vec<T> {
match kind {
index::Kind::Other => &self.other,
index::Kind::Function => &self.functions,
index::Kind::GlobalDecl => &self.global_decls,
index::Kind::Error => &self.errors,
index::Kind::File => &self.files,
}
}
fn table_len(&self, kind: index::Kind) -> usize {
self.table_ref(kind).len()
}
fn iter(&self) -> impl Iterator<Item = &T> {
self.errors
.iter()
.chain(self.files.iter())
.chain(self.functions.iter())
.chain(self.global_decls.iter())
.chain(self.other.iter())
}
}
pub struct Ast {
tags: AstTables<Tag>,
datas: AstTables<Data>,
source_locs: AstTables<SourceLocation>,
extra: Vec<u32>,
}
impl Debug for Ast {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Ast")
.field_with("nodes", |f| {
let mut list = f.debug_list();
struct LocDisplay(SourceLocation);
impl Debug for LocDisplay {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({})", self.0)
}
}
let entries = self
.tags
.iter()
.cloned()
.zip(self.datas.iter().cloned())
.zip(self.source_locs.iter().cloned())
.enumerate()
.map(|(i, ((tag, data), loc))| {
(i, tag, ExpandedData::from((tag, data)), LocDisplay(loc))
});
list.entries(entries).finish()
})
.field("extra", &self.extra)
.finish()
}
}
impl Ast {
fn new() -> Ast {
let mut tags = AstTables::new();
let mut datas = AstTables::new();
let mut source_locs = AstTables::new();
tags.other.push(Tag::Root);
datas.other.push(Data::extra_range(0, 0));
source_locs.other.push(SourceLocation::new(0, 0));
Self {
tags,
datas,
extra: vec![],
source_locs,
}
}
fn reserve_node_other(&mut self) -> Index {
self.reserve_node(Kind::Other)
}
fn reserve_node(&mut self, kind: index::Kind) -> Index {
let i = self.tags.reserve(kind);
self.datas.reserve(kind);
self.source_locs.reserve(kind);
i
}
fn get_loc(&self, index: Index) -> SourceLocation {
self.source_locs[index]
}
fn push_error(&mut self, error: ParseError, loc: SourceLocation) -> Index {
let i = self.reserve_node(Kind::Error);
self.set_tag_data_source_loc(i, Tag::Error, Data::error(error), loc);
i
}
fn set_file<I: IntoIterator<Item = Index>>(&mut self, i: Index, decls: I, loc: SourceLocation) {
let (extra_start, extra_end) = self.extend_extra_by_indices(decls);
self.set_tag_data_source_loc(i, Tag::File, Data::extra_range(extra_start, extra_end), loc);
}
fn push_file<I: IntoIterator<Item = Index>>(&mut self, decls: I, loc: SourceLocation) -> Index {
let i = self.reserve_node(Kind::File);
self.set_file(i, decls, loc);
i
}
fn set_root<I: IntoIterator<Item = Index>>(&mut self, decls: I) {
let (extra_start, extra_end) = self.extend_extra_by_indices(decls);
self.tags.other[0] = Tag::Root;
self.datas.other[0] = Data::extra_range(extra_start, extra_end);
}
fn get_root_file_indices<'a>(&'a self) -> impl Iterator<Item = Index> + 'a {
let (a, b) = self.datas.other[0].as_extra_range();
self.extra[a..b]
.iter()
.cloned()
.map(|i| Index::from_u32(i).unwrap())
}
fn push_global_decl(
&mut self,
ident: intern::Index,
ty: Index,
expr: Index,
loc: SourceLocation,
) -> Index {
let i = self.reserve_node(Kind::GlobalDecl);
let (extra_start, _) = self.extend_extra([ty.into_u32(), expr.into_u32()]);
self.set_tag_data_source_loc(
i,
Tag::GlobalDecl,
Data::intern_and_extra_offset(ident, extra_start),
loc,
);
i
}
fn set_fn_decl(&mut self, i: Index, proto: Index, body: Index, loc: SourceLocation) {
self.set_tag_data_source_loc(i, Tag::FunctionDecl, Data::two_indices(proto, body), loc);
}
fn push_fn_decl(&mut self, proto: Index, body: Index, loc: SourceLocation) -> Index {
let i = self.reserve_node(Kind::Function);
self.set_fn_decl(i, proto, body, loc);
i
}
fn push_ret(&mut self, expr: Option<Index>, loc: SourceLocation) -> Index {
let i = self.reserve_node(Kind::Other);
match expr {
Some(expr) => {
self.set_tag_data_source_loc(i, Tag::ReturnExprStmt, Data::index(expr), loc)
}
None => self.set_tag_data_source_loc(i, Tag::ReturnStmt, Data::none(), loc),
}
i
}
fn push_var_decl(
&mut self,
is_let: bool,
name: intern::Index,
ty: Option<Index>,
assignment: Option<Index>,
loc: SourceLocation,
) -> Index {
let i = self.reserve_node_other();
let start = self.extra.len() as u32;
self.extra.push(name.into_u32());
_ = self.extend_extra(assignment.map(|i| i.into_u32()));
_ = self.extend_extra(ty.map(|i| i.into_u32()));
let end = self.extra.len() as u32;
let tag = match (is_let, assignment.is_some()) {
(true, false) => Tag::VarDecl,
(true, true) => Tag::VarDeclAssignment,
(false, false) => Tag::MutVarDecl,
(false, true) => Tag::MutVarDeclAssignment,
};
self.set_tag_data_source_loc(i, tag, Data::extra_range(start, end), loc);
i
}
fn push_fn_proto(
&mut self,
ident: intern::Index,
return_type: Index,
parameter_list: Index,
loc: SourceLocation,
) -> Index {
let i = self.reserve_node_other();
let (extra_start, _) =
self.extend_extra([return_type.into_u32(), parameter_list.into_u32()]);
self.set_tag_data_source_loc(
i,
Tag::FunctionProto,
Data::intern_and_extra_offset(ident, extra_start),
loc,
);
i
}
fn set_block<I: IntoIterator<Item = Index>>(
&mut self,
i: Index,
statements: I,
trailing: Option<Index>,
loc: SourceLocation,
) {
let (extra_start, extra_end) =
self.extend_extra_by_indices(statements.into_iter().chain(trailing.into_iter()));
if trailing.is_some() {
self.set_tag_data_source_loc(
i,
Tag::BlockTrailingExpr,
Data::extra_range(extra_start, extra_end),
loc,
);
} else {
self.set_tag_data_source_loc(
i,
Tag::Block,
Data::extra_range(extra_start, extra_end),
loc,
);
}
}
fn push_block<I: IntoIterator<Item = Index>>(
&mut self,
statements: I,
trailing: Option<Index>,
loc: SourceLocation,
) -> Index {
let i = self.reserve_node_other();
self.set_block(i, statements, trailing, loc);
i
}
fn push_parameter_list<I: IntoIterator<Item = Index>>(
&mut self,
parameters: I,
loc: SourceLocation,
) -> Index {
let i = self.reserve_node_other();
let (extra_start, extra_end) = self.extend_extra_by_indices(parameters);
self.set_tag_data_source_loc(
i,
Tag::ParameterList,
Data::extra_range(extra_start, extra_end),
loc,
);
i
}
fn push_argument(&mut self, expr: Index, loc: SourceLocation) -> Index {
let i = self.reserve_node_other();
self.set_tag_data_source_loc(i, Tag::Argument, Data::index(expr), loc);
i
}
fn push_named_argument(
&mut self,
name: intern::Index,
expr: Index,
loc: SourceLocation,
) -> Index {
let i = self.reserve_node_other();
self.set_tag_data_source_loc(
i,
Tag::NamedArgument,
Data::index_and_intern(expr, name),
loc,
);
i
}
fn push_parameter(&mut self, name: intern::Index, ty: Index, loc: SourceLocation) -> Index {
let i = self.reserve_node_other();
self.set_tag_data_source_loc(i, Tag::Parameter, Data::index_and_intern(ty, name), loc);
i
}
fn push_argument_list<I: IntoIterator<Item = Index>>(
&mut self,
args: I,
loc: SourceLocation,
) -> Index {
let i = self.reserve_node_other();
let (extra_start, extra_end) = self.extend_extra_by_indices(args);
self.set_tag_data_source_loc(
i,
Tag::ArgumentList,
Data::extra_range(extra_start, extra_end),
loc,
);
i
}
fn push_unary(&mut self, tag: Tag, lhs: Index, loc: SourceLocation) -> PlaceOrValue {
let i = self.reserve_node_other();
self.set_tag_data_source_loc(i, tag, Data::index(lhs), loc);
match tag {
Tag::Deref => PlaceOrValue::Place(i),
_ => PlaceOrValue::Value(i),
}
}
/// converts from a place expression to a value expression.
fn push_place_to_value_conversion(&mut self, index: Index) -> Index {
let loc = self.get_loc(index);
let i = self.reserve_node_other();
self.set_tag_data_source_loc(index, Tag::PlaceToValueConversion, Data::index(index), loc);
i
}
/// converts from a value expression to a place expression.
fn push_value_to_place_conversion(&mut self, index: Index) -> Index {
let loc = self.get_loc(index);
let i = self.reserve_node_other();
self.set_tag_data_source_loc(index, Tag::ValueToPlaceConversion, Data::index(index), loc);
i
}
fn push_binary(&mut self, tag: Tag, lhs: Index, rhs: Index, loc: SourceLocation) -> Index {
let i = self.reserve_node_other();
self.set_tag_data_source_loc(i, tag, Data::two_indices(lhs, rhs), loc);
i
}
fn push_assign(&mut self, lhs: Index, rhs: Index, loc: SourceLocation) -> Index {
let i = self.reserve_node_other();
self.set_tag_data_source_loc(i, Tag::Assign, Data::two_indices(lhs, rhs), loc);
i
}
fn push_cast(&mut self, lhs: Index, ty: Index, loc: SourceLocation) -> Index {
let i = self.reserve_node_other();
self.set_tag_data_source_loc(i, Tag::ExplicitCast, Data::two_indices(lhs, ty), loc);
i
}
fn push_if(&mut self, cond: Index, body: Index, loc: SourceLocation) -> Index {
let i = self.reserve_node_other();
self.set_tag_data_source_loc(i, Tag::IfExpr, Data::two_indices(cond, body), loc);
i
}
fn push_if_else(
&mut self,
cond: Index,
body: Index,
other: Index,
loc: SourceLocation,
) -> Index {
let i = self.reserve_node_other();
let (extra_start, _) = self.extend_extra_by_indices([body, other]);
self.set_tag_data_source_loc(
i,
Tag::IfElseExpr,
Data::index_and_extra_offset(cond, extra_start),
loc,
);
i
}
fn push_call_expr(&mut self, lhs: Index, args: Index, loc: SourceLocation) -> Index {
let i = self.reserve_node_other();
self.set_tag_data_source_loc(i, Tag::CallExpr, Data::two_indices(lhs, args), loc);
i
}
fn push_decl_ref_unresolved(
&mut self,
scope: Index,
ident: intern::Index,
loc: SourceLocation,
) -> Index {
let i = self.reserve_node_other();
self.set_tag_data_source_loc(
i,
Tag::DeclRefUnresolved,
Data::index_and_intern(scope, ident),
loc,
);
i
}
fn resolve_decl_ref(&mut self, i: Index, decl: Index) {
self.tags[i] = Tag::DeclRef;
self.datas[i] = Data::index(decl);
}
fn push_struct_decl<I: IntoIterator<Item = (intern::Index, Index)>>(
&mut self,
name: intern::Index,
flags: StructFlags,
fields: I,
loc: SourceLocation,
) -> Index {
let i = self.reserve_node(Kind::GlobalDecl);
self.set_struct_decl(i, name, flags, fields, loc)
}
fn set_struct_decl<I: IntoIterator<Item = (intern::Index, Index)>>(
&mut self,
i: Index,
name: intern::Index,
flags: StructFlags,
fields: I,
loc: SourceLocation,
) -> Index {
let (offset, _) = self.extend_extra([flags.pack()]);
let (names, types) = fields
.into_iter()
.map(|(name, ty)| (name.into_u32(), ty.into_u32()))
.unzip::<_, _, Vec<_>, Vec<_>>();
self.extend_extra(types);
self.extend_extra(names);
self.set_tag_data_source_loc(
i,
Tag::StructDecl,
Data::intern_and_extra_offset(name, offset),
loc,
);
i
}
fn push_field_decl(&mut self, name: intern::Index, ty: Index, loc: SourceLocation) -> Index {
let i = self.reserve_node_other();
self.set_tag_data_source_loc(i, Tag::FieldDecl, Data::index_and_intern(ty, name), loc);
i
}
fn push_field_access(
&mut self,
expr: Index,
name: intern::Index,
loc: SourceLocation,
) -> PlaceOrValue {
let i = self.reserve_node_other();
self.set_tag_data_source_loc(i, Tag::FieldAccess, Data::index_and_intern(expr, name), loc);
PlaceOrValue::Place(i)
}
fn push_interend_type(&mut self, ty: intern::Index, loc: SourceLocation) -> Index {
let i = self.reserve_node_other();
self.set_tag_data_source_loc(i, Tag::InternedType, Data::intern(ty), loc);
i
}
fn push_array_type(
&mut self,
length_expr: Index,
pointer_ty: Index,
loc: SourceLocation,
) -> Index {
let i = self.reserve_node_other();
self.set_tag_data_source_loc(
i,
Tag::ArrayType,
Data::two_indices(length_expr, pointer_ty),
loc,
);
i
}
fn push_pointer_type(&mut self, ty: Index, flags: PointerFlags, loc: SourceLocation) -> Index {
let i = self.reserve_node_other();
self.set_tag_data_source_loc(
i,
Tag::PointerType,
Data::index_and_extra_offset(ty, flags.pack() as u32),
loc,
);
i
}
fn push_type_ref_unresolved(
&mut self,
scope: Index,
ident: intern::Index,
loc: SourceLocation,
) -> Index {
let i = self.reserve_node_other();
self.set_tag_data_source_loc(
i,
Tag::TypeDeclRefUnresolved,
Data::index_and_intern(scope, ident),
loc,
);
i
}
fn resolve_type_ref(&mut self, i: Index, decl: Index) {
self.tags[i] = Tag::TypeDeclRef;
self.datas[i] = Data::index(decl);
}
fn push_expr_stmt(&mut self, expr: Index) -> Index {
let i = self.reserve_node_other();
let loc = self.get_loc(expr);
self.set_tag_data_source_loc(i, Tag::ExprStmt, Data::index(expr), loc);
i
}
fn push_constant(&mut self, value: intern::Index, ty: Index, loc: SourceLocation) -> Index {
let i = self.reserve_node_other();
self.set_tag_data_source_loc(i, Tag::Constant, Data::index_and_intern(ty, value), loc);
i
}
fn extend_extra_by_indices<I: IntoIterator<Item = Index>>(&mut self, indices: I) -> (u32, u32) {
self.extend_extra(indices.into_iter().map(|i| i.as_u32()))
}
fn extend_extra<I: IntoIterator<Item = u32>>(&mut self, words: I) -> (u32, u32) {
let i = self.extra.len() as u32;
self.extra.extend(words);
(i, self.extra.len() as u32)
}
fn set_tag_data_source_loc(&mut self, index: Index, tag: Tag, data: Data, loc: SourceLocation) {
self.tags[index] = tag;
self.datas[index] = data;
self.source_locs[index] = loc;
}
}
struct Children(Vec<Index>);
impl Display for Children {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[")?;
if let Some((last, rest)) = self.0.split_last() {
for i in rest {
write!(f, "{i}, ")?;
}
write!(f, "{last}")?;
}
write!(f, "]")
}
}
type TypeCache = BTreeMap<Index, intern::Index>;
// type ComptimeCache = BTreeMap<Index, bool>;
#[derive(Debug, Default)]
struct ComptimeCache {
inner: BTreeMap<Index, bool>,
// this is (a,b) where b dominates a
// meaning:
// when a is marked as runtime, b can be updated to be runtime as well.
dependencies: BTreeMap<Index, Index>,
}
impl ComptimeCache {
fn get(&self, key: &Index) -> Option<bool> {
self.inner.get(key).cloned()
}
fn insert(&mut self, key: Index, value: bool) {
self.inner.insert(key, value);
if !value {
self.set_runtime(key);
}
}
fn mark_as_dominated_by(&mut self, a: Index, b: Index) {
self.dependencies.insert(a, b);
}
fn set_runtime(&mut self, key: Index) {
self.inner.insert(key, false);
if let Some(&dom) = self.dependencies.get(&key) {
self.set_runtime(dom);
}
}
}
impl Ast {
fn get_type_of_node(
&self,
ip: &InternPool,
cache: &mut TypeCache,
index: Index,
) -> intern::Index {
if let Some(ty) = cache.get(&index) {
return *ty;
}
let void = ip.get_void_type();
let tag = self.tags[index];
let data = self.datas[index];
let ty = match tag {
Tag::ArgumentList
| Tag::ExprStmt
| Tag::ReturnExprStmt
| Tag::Block
| Tag::ParameterList
| Tag::File => void,
// these all evaluate to pointers
Tag::VarDecl | Tag::MutVarDecl | Tag::VarDeclAssignment | Tag::MutVarDeclAssignment => {
let (_, b) = data.as_extra_range();
let pointee =
self.get_type_of_node(ip, cache, Index::from_u32(self.extra[b - 1]).unwrap());
ip.try_get_pointer_type(
pointee,
Some(PointerFlags::new(tag == Tag::VarDecl, false, false)),
)
.unwrap()
}
// these all evaluate to pointers
Tag::GlobalDecl => {
let (_, a) = data.as_intern_and_extra_offset();
let pointee =
self.get_type_of_node(ip, cache, Index::from_u32(self.extra[a]).unwrap());
ip.try_get_pointer_type(pointee, Some(PointerFlags::new(true, false, true)))
.unwrap()
}
Tag::FunctionDecl => self.get_type_of_node(ip, cache, data.as_two_indices().0),
Tag::FunctionProto => {
let (_, i) = data.as_intern_and_extra_offset();
let (return_type, parameter_list) = (
Index::from_u32(self.extra[i]).unwrap(),
Index::from_u32(self.extra[i + 1]).unwrap(),
);
let return_type = self.datas[return_type].as_intern();
let parameters = {
let (a, b) = self.datas[parameter_list].as_extra_range();
self.extra[a..b]
.iter()
.map(|&i| Index::from_u32(i).unwrap())
.map(|i| {
// i is index to a parameter, a parameter is (index, intern)
let ty = self.datas[i].as_index_intern().0;
self.datas[ty].as_intern()
})
};
ip.try_get_function_type(return_type, parameters).unwrap()
}
Tag::BlockTrailingExpr => {
let (_a, b) = data.as_extra_range();
self.get_type_of_node(ip, cache, Index::from_u32(self.extra[b - 1]).unwrap())
}
Tag::CallExpr => {
let (expr, _args) = data.as_two_indices();
let fn_ty = self.get_type_of_node(ip, cache, expr);
if let intern::Key::FunctionType { return_type, .. } = ip.get_key(fn_ty) {
return_type
} else {
eprintln!(
"lhs of call expr is not a function: {fn_ty}: {:?}",
ip.get_key(fn_ty)
);
void
}
}
Tag::Argument => self.get_type_of_node(ip, cache, data.as_index()),
Tag::NamedArgument => {
let (a, _) = data.as_index_intern();
self.get_type_of_node(ip, cache, a)
}
Tag::ExplicitCast => {
let (_, a) = data.as_two_indices();
self.get_type_of_node(ip, cache, a)
}
// this evaluates to a pointer
Tag::FieldAccess => {
let (ty_expr, name) = data.as_index_intern();
let ty = self.get_type_of_node(ip, cache, ty_expr);
let pointee = match ip.get_key(ty) {
intern::Key::PointerType { pointee, .. }
if let intern::Key::StructType { fields, .. } = ip.get_key(pointee) =>
{
fields
.iter()
.cloned()
.find(|(n, _)| n == &name)
.map(|(_, t)| t)
.unwrap_or(void)
}
intern::Key::StructType { fields, .. } => fields
.iter()
.cloned()
.find(|(n, _)| n == &name)
.map(|(_, t)| t)
.unwrap_or(void),
_ => {
unimplemented!()
}
};
ip.try_get_pointer_type(pointee, None).unwrap()
}
// this evaluates to a pointer
Tag::SubscriptExpr => {
let ty = self.get_type_of_node(ip, cache, data.as_two_indices().0);
match ip.get_key(ty) {
intern::Key::PointerType { .. } | intern::Key::ArrayType { .. } => {
// note: because of value-to-place and place-to-value,
// this is actually a pointer.
// pointee
ty
}
_ => {
eprintln!("lhs of subscript is not an array or pointer!");
void
}
}
}
// this evaluates to a pointer
Tag::AddressOf => {
let ty = self.get_type_of_node(ip, cache, data.as_index());
// TODO: find out of the expression is const, volatile for flags
// ip.try_get_pointer_type(ty, None).unwrap()
// note: because of value-to-place and place-to-value,
// this is actually the same type.
ty
}
Tag::Deref => {
let ty = self.get_type_of_node(ip, cache, data.as_index());
if let intern::Key::PointerType { pointee, .. } = ip.get_key(ty) {
pointee
} else {
eprintln!("lhs of deref is not a pointer!");
void
}
}
Tag::ValueToPlaceConversion => {
let ty = self.get_type_of_node(ip, cache, data.as_index());
ip.try_get_pointer_type(ty, None).unwrap()
}
Tag::PlaceToValueConversion => {
let ty = self.get_type_of_node(ip, cache, data.as_index());
ip.try_get_pointee_type(ty).unwrap()
}
Tag::Not | Tag::Negate => self.get_type_of_node(ip, cache, data.as_index()),
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 => self.get_type_of_node(ip, cache, data.as_two_indices().0),
Tag::IfExpr => ip.get_bool_type(), // really?
Tag::IfElseExpr => {
let (_, b) = data.as_index_and_extra_offset();
let if_ = Index::from_u32(self.extra[b]).unwrap();
self.get_type_of_node(ip, cache, if_)
}
Tag::Constant | Tag::Parameter => {
self.get_type_of_node(ip, cache, data.as_index_intern().0)
}
Tag::DeclRef => self.get_type_of_node(ip, cache, data.as_index()),
Tag::StructDecl => {
let (name, _) = data.as_intern_and_extra_offset();
ip.try_get_struct_type(name, index).unwrap()
}
Tag::Assign
| Tag::Root
| Tag::DeclRefUnresolved
| Tag::Error
| Tag::Undefined
| Tag::ReturnStmt => void,
Tag::FieldDecl => self.get_type_of_node(ip, cache, data.as_index_intern().0),
Tag::InternedType => data.as_intern(),
Tag::TypeDeclRef | Tag::TypeDeclRefUnresolved | Tag::PointerType | Tag::ArrayType => {
ip.get_void_type()
}
};
cache.insert(index, ty);
ty
}
fn get_node_children(&self, index: Index) -> Vec<Index> {
let tag = self.tags[index];
let data = self.datas[index];
match tag {
Tag::File => {
let (a, b) = data.as_extra_range();
self.extra[a..b]
.iter()
.map(|&i| Index::from_u32(i).unwrap())
.collect()
}
Tag::FunctionProto => {
let (_, i) = data.as_intern_and_extra_offset();
self.extra[i..=i + 1]
.iter()
.map(|&i| Index::from_u32(i).unwrap())
.collect()
}
Tag::FunctionDecl => {
let (a, b) = data.as_two_indices();
vec![a, b]
}
Tag::ParameterList => {
let (a, b) = data.as_extra_range();
self.extra[a..b]
.iter()
.map(|&i| Index::from_u32(i).unwrap())
.collect()
}
Tag::Block | Tag::BlockTrailingExpr => {
let (a, b) = data.as_extra_range();
self.extra[a..b]
.iter()
.map(|&i| Index::from_u32(i).unwrap())
.collect()
}
Tag::ExprStmt | Tag::ReturnExprStmt => {
let a = data.as_index();
vec![a]
}
Tag::VarDeclAssignment | Tag::MutVarDeclAssignment => {
let (a, b) = data.as_extra_range();
self.extra[a + 1..b]
.iter()
.map(|&i| Index::from_u32(i).unwrap())
.collect()
}
Tag::GlobalDecl => {
let (_, offset) = data.as_intern_and_extra_offset();
self.extra[offset..=offset + 1]
.iter()
.map(|&i| Index::from_u32(i).unwrap())
.collect()
}
Tag::CallExpr | Tag::ExplicitCast => {
let (a, b) = data.as_two_indices();
vec![a, b]
}
Tag::ArgumentList => {
let (a, b) = data.as_extra_range();
self.extra[a..b]
.iter()
.map(|&i| Index::from_u32(i).unwrap())
.collect()
}
Tag::Argument => {
let a = data.as_index();
vec![a]
}
Tag::FieldDecl | Tag::FieldAccess | Tag::NamedArgument => {
let (a, _) = data.as_index_intern();
vec![a]
}
Tag::ValueToPlaceConversion
| Tag::PlaceToValueConversion
| Tag::Deref
| Tag::AddressOf
| Tag::Not
| Tag::Negate => {
let a = data.as_index();
vec![a]
}
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
| Tag::Assign
| Tag::SubscriptExpr
| Tag::ArrayType
| Tag::IfExpr => {
let (a, b) = data.as_two_indices();
vec![a, b]
}
Tag::IfElseExpr => {
let (a, b) = data.as_index_and_extra_offset();
let if_ = Index::from_u32(self.extra[b]).unwrap();
let else_ = Index::from_u32(self.extra[b + 1]).unwrap();
vec![a, if_, else_]
}
Tag::PointerType => {
let (a, _) = data.as_index_and_extra_offset();
vec![a]
}
Tag::StructDecl => {
let (_a, offset) = data.as_intern_and_extra_offset();
let flags = StructFlags::unpack(self.extra[offset]);
self.extra[offset + 1..(offset + 1 + flags.num_fields as usize)]
.iter()
.map(|&i| Index::from_u32(i).unwrap())
.collect()
}
Tag::InternedType
| Tag::Root
| Tag::TypeDeclRefUnresolved
| Tag::DeclRefUnresolved
| Tag::Error
| Tag::Undefined
| Tag::TypeDeclRef
| Tag::DeclRef
| Tag::ReturnStmt => vec![],
Tag::Parameter | Tag::Constant => {
let (a, _) = data.as_index_intern();
vec![a]
}
Tag::VarDecl | Tag::MutVarDecl => {
let (a, _) = data.as_extra_range();
vec![Index::from_u32(self.extra[a + 1]).unwrap()]
}
}
}
fn is_node_comptime_evaluable(&self, cache: &mut ComptimeCache, index: Index) -> bool {
if let Some(a) = cache.get(&index) {
a
} else {
let tag = self.tags[index];
let data = self.datas[index];
let children = self.get_node_children(index);
let are_children_comptime = |this: &Self, cache: &mut ComptimeCache| {
children
.iter()
.all(|&i| this.is_node_comptime_evaluable(cache, i))
};
let is_comptime = match tag {
Tag::Parameter => false,
// TODO: figure out if there are function protos that arent const
// Tag::FunctionProto | Tag::FunctionDecl | Tag::Block | Tag::BlockTrailingExpr => {
// are_children_comptime(self, cache)
// }
Tag::Constant => true,
Tag::ReturnStmt => true,
Tag::ReturnExprStmt => are_children_comptime(self, cache),
Tag::MutVarDecl | Tag::MutVarDeclAssignment => false,
// Tag::VarDecl => true,
// Tag::ValueToPlaceConversion => false,
Tag::PlaceToValueConversion | Tag::VarDeclAssignment => {
are_children_comptime(self, cache)
}
Tag::GlobalDecl => true,
Tag::StructDecl => true,
Tag::FieldDecl => true,
Tag::DeclRef => self.tags[data.as_index()] == Tag::GlobalDecl,
Tag::InternedType | Tag::PointerType | Tag::ArrayType | Tag::TypeDeclRef => true,
// Tag::CallExpr => are_children_comptime(self, cache),
// Tag::FieldAccess => {
// let parent = data.as_index_intern().0;
// cache.mark_as_dominated_by(index, parent);
// self.is_node_comptime_evaluable(cache, parent)
// }
Tag::ArgumentList => are_children_comptime(self, cache),
Tag::Argument => self.is_node_comptime_evaluable(cache, data.as_index()),
Tag::NamedArgument => {
self.is_node_comptime_evaluable(cache, data.as_index_intern().0)
}
Tag::ExplicitCast => {
self.is_node_comptime_evaluable(cache, data.as_two_indices().0)
}
Tag::Deref | Tag::AddressOf => false,
Tag::Not
| Tag::Negate
| 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::SubscriptExpr // TODO: add array decl expression
| Tag::Rem => are_children_comptime(self, cache),
Tag::Assign => {
let (left, _) = data.as_two_indices();
cache.mark_as_dominated_by(index, left);
are_children_comptime(self, cache)
}
// Tag::IfExpr | Tag::IfElseExpr => are_children_comptime(self, cache),
Tag::Root
| Tag::File
| Tag::ParameterList
| Tag::ExprStmt
| Tag::DeclRefUnresolved
| Tag::TypeDeclRefUnresolved
| Tag::Error
| Tag::Undefined => false,
_ => false,
};
cache.insert(index, is_comptime);
is_comptime
}
}
fn comptime_value_of_node(
&self,
ip: &InternPool,
pointer_bits: u16,
cache: &mut TypeCache,
index: Index,
) -> Option<crate::comptime::ComptimeNumber> {
let tag = self.tags[index];
let data = self.datas[index];
match tag {
Tag::Constant => {
let (ty, value) = data.as_index_intern();
let ty = self.get_type_of_node(ip, cache, ty);
Some(interned_type_and_value_to_comptime_number(
ip,
pointer_bits,
ty,
value,
))
}
Tag::GlobalDecl => {
let (_, offset) = data.as_intern_and_extra_offset();
self.comptime_value_of_node(
ip,
pointer_bits,
cache,
Index::from_u32(self.extra[offset + 1]).unwrap(),
)
}
Tag::VarDeclAssignment => {
let (a, _) = data.as_extra_range();
self.comptime_value_of_node(
ip,
pointer_bits,
cache,
Index::from_u32(self.extra[a + 1]).unwrap(),
)
}
Tag::SubscriptExpr => {
todo!()
}
Tag::PlaceToValueConversion | Tag::DeclRef => {
self.comptime_value_of_node(ip, pointer_bits, cache, data.as_index())
}
Tag::ExplicitCast => {
let (expr, ty) = data.as_two_indices();
let ty = ip.as_ast1_type(intern::AMD64_POINTER_BITS, self.datas[ty].as_intern());
let val = self.comptime_value_of_node(ip, pointer_bits, cache, expr);
val.and_then(|i| i.explicit_cast(ty).ok())
}
Tag::Add => {
let (a, b) = data.as_two_indices();
self.comptime_value_of_node(ip, pointer_bits, cache, a)
.and_then(|a| {
self.comptime_value_of_node(ip, pointer_bits, cache, b)
.and_then(|b| a.add(b).ok())
})
}
Tag::Sub => {
let (a, b) = data.as_two_indices();
self.comptime_value_of_node(ip, pointer_bits, cache, a)
.and_then(|a| {
self.comptime_value_of_node(ip, pointer_bits, cache, b)
.and_then(|b| a.sub(b).ok())
})
}
Tag::Mul => {
let (a, b) = data.as_two_indices();
self.comptime_value_of_node(ip, pointer_bits, cache, a)
.and_then(|a| {
self.comptime_value_of_node(ip, pointer_bits, cache, b)
.and_then(|b| a.mul(b).ok())
})
}
Tag::Div => {
let (a, b) = data.as_two_indices();
self.comptime_value_of_node(ip, pointer_bits, cache, a)
.and_then(|a| {
self.comptime_value_of_node(ip, pointer_bits, cache, b)
.and_then(|b| a.div(b).ok())
})
}
Tag::Rem => {
let (a, b) = data.as_two_indices();
self.comptime_value_of_node(ip, pointer_bits, cache, a)
.and_then(|a| {
self.comptime_value_of_node(ip, pointer_bits, cache, b)
.and_then(|b| a.rem(b).ok())
})
}
Tag::Shl => {
let (a, b) = data.as_two_indices();
self.comptime_value_of_node(ip, pointer_bits, cache, a)
.and_then(|a| {
self.comptime_value_of_node(ip, pointer_bits, cache, b)
.and_then(|b| a.shl(b).ok())
})
}
Tag::Shr => {
let (a, b) = data.as_two_indices();
self.comptime_value_of_node(ip, pointer_bits, cache, a)
.and_then(|a| {
self.comptime_value_of_node(ip, pointer_bits, cache, b)
.and_then(|b| a.shr(b).ok())
})
}
Tag::BitAnd => {
let (a, b) = data.as_two_indices();
self.comptime_value_of_node(ip, pointer_bits, cache, a)
.and_then(|a| {
self.comptime_value_of_node(ip, pointer_bits, cache, b)
.and_then(|b| a.bitand(b).ok())
})
}
Tag::BitOr => {
let (a, b) = data.as_two_indices();
self.comptime_value_of_node(ip, pointer_bits, cache, a)
.and_then(|a| {
self.comptime_value_of_node(ip, pointer_bits, cache, b)
.and_then(|b| a.bitor(b).ok())
})
}
Tag::BitXOr => {
let (a, b) = data.as_two_indices();
self.comptime_value_of_node(ip, pointer_bits, cache, a)
.and_then(|a| {
self.comptime_value_of_node(ip, pointer_bits, cache, b)
.and_then(|b| a.bitxor(b).ok())
})
}
Tag::And => {
let (a, b) = data.as_two_indices();
self.comptime_value_of_node(ip, pointer_bits, cache, a)
.and_then(|a| {
self.comptime_value_of_node(ip, pointer_bits, cache, b)
.and_then(|b| a.and(b).ok())
})
}
Tag::Or => {
let (a, b) = data.as_two_indices();
self.comptime_value_of_node(ip, pointer_bits, cache, a)
.and_then(|a| {
self.comptime_value_of_node(ip, pointer_bits, cache, b)
.and_then(|b| a.or(b).ok())
})
}
Tag::Eq => {
let (a, b) = data.as_two_indices();
self.comptime_value_of_node(ip, pointer_bits, cache, a)
.and_then(|a| {
self.comptime_value_of_node(ip, pointer_bits, cache, b)
.and_then(|b| a.eq(b).ok())
})
}
Tag::NEq => {
let (a, b) = data.as_two_indices();
self.comptime_value_of_node(ip, pointer_bits, cache, a)
.and_then(|a| {
self.comptime_value_of_node(ip, pointer_bits, cache, b)
.and_then(|b| a.eq(b).and_then(|i| i.not()).ok())
})
}
Tag::Gt => {
let (a, b) = data.as_two_indices();
self.comptime_value_of_node(ip, pointer_bits, cache, a)
.and_then(|a| {
self.comptime_value_of_node(ip, pointer_bits, cache, b)
.and_then(|b| a.gt(b).ok())
})
}
Tag::Lt => {
let (a, b) = data.as_two_indices();
self.comptime_value_of_node(ip, pointer_bits, cache, a)
.and_then(|a| {
self.comptime_value_of_node(ip, pointer_bits, cache, b)
.and_then(|b| a.lt(b).ok())
})
}
Tag::Le => {
let (a, b) = data.as_two_indices();
self.comptime_value_of_node(ip, pointer_bits, cache, a)
.and_then(|a| {
self.comptime_value_of_node(ip, pointer_bits, cache, b)
.and_then(|b| a.le(b).ok())
})
}
Tag::Ge => {
let (a, b) = data.as_two_indices();
self.comptime_value_of_node(ip, pointer_bits, cache, a)
.and_then(|a| {
self.comptime_value_of_node(ip, pointer_bits, cache, b)
.and_then(|b| a.ge(b).ok())
})
}
Tag::Not => {
let a = data.as_index();
self.comptime_value_of_node(ip, pointer_bits, cache, a)
.and_then(|a| a.not().ok())
}
Tag::Negate => {
let a = data.as_index();
self.comptime_value_of_node(ip, pointer_bits, cache, a)
.and_then(|a| a.neg().ok())
}
_ => None,
}
}
}
pub fn comptime_number_to_interned_type_and_value(
ip: &mut InternPool,
pointer_bits: u16,
comptime: ComptimeNumber,
) -> (intern::Index, intern::Index) {
use crate::ast::IntegralType;
let (value, ty) = comptime.into_bytes_and_type();
let value = match ty {
crate::ast::Type::Bool => {
if value.get(0) != Some(&1) {
ip.get_false_value()
} else {
ip.get_true_value()
}
}
crate::ast::Type::Integer(IntegralType { bits: 65.., .. })
| crate::ast::Type::ComptimeNumber => {
let bigint = BigInt::from_signed_bytes_le(&value);
if bigint.sign() == num_bigint::Sign::Minus {
ip.get_or_insert(intern::Key::NegativeInt { bigint })
} else {
ip.get_or_insert(intern::Key::PositiveInt { bigint })
}
}
crate::ast::Type::Integer(i) => match i.bits {
..=32 => {
let mut buf = [0u8; 4];
buf[..value.len()].copy_from_slice(&value[..]);
if i.signed {
ip.get_or_insert(intern::Key::SIntSmall {
bits: i32::from_le_bytes(buf),
})
} else {
ip.get_or_insert(intern::Key::UIntSmall {
bits: u32::from_le_bytes(buf),
})
}
}
..=64 => {
let mut buf = [0u8; 8];
buf[..value.len()].copy_from_slice(&value[..]);
if i.signed {
ip.get_or_insert(intern::Key::SInt64 {
bits: i64::from_le_bytes(buf),
})
} else {
ip.get_or_insert(intern::Key::UInt64 {
bits: u64::from_le_bytes(buf),
})
}
}
_ => unreachable!(),
},
crate::ast::Type::Floating(FloatingType::Binary32) => {
let mut buf = [0u8; 4];
buf[..value.len()].copy_from_slice(&value[..]);
ip.get_or_insert(intern::Key::F32 {
bits: f32::from_le_bytes(buf),
})
}
crate::ast::Type::Floating(FloatingType::Binary64) => {
let mut buf = [0u8; 8];
buf[..value.len()].copy_from_slice(&value[..]);
ip.get_or_insert(intern::Key::F64 {
bits: f64::from_le_bytes(buf),
})
}
_ => unimplemented!(),
};
let ty = ip.from_ast1_type(pointer_bits, &ty);
(value, ty)
}
pub fn interned_type_and_value_to_comptime_number(
ip: &InternPool,
pointer_bits: u16,
ty: intern::Index,
val: intern::Index,
) -> crate::comptime::ComptimeNumber {
use crate::ast::IntegralType;
use crate::comptime::*;
let ty_key = ip.get_key(ty);
let signed = ip.is_type_signed(ty, AMD64_POINTER_TYPE_INFO);
match ty_key {
intern::Key::SIntType { bit_width: bits } | intern::Key::UIntType { bit_width: bits } => {
let ty = IntegralType::new(signed, bits);
match ip.get_key(val) {
intern::Key::SIntSmall { bits } => ComptimeNumber::Integral(ComptimeInt::Native {
bits: bits as _,
ty,
}),
intern::Key::UIntSmall { bits } => ComptimeNumber::Integral(ComptimeInt::Native {
bits: bits as _,
ty,
}),
intern::Key::SInt64 { bits } => ComptimeNumber::Integral(ComptimeInt::Native {
bits: bits as _,
ty,
}),
intern::Key::UInt64 { bits } => ComptimeNumber::Integral(ComptimeInt::Native {
bits: bits as _,
ty,
}),
intern::Key::PositiveInt { bigint } => {
ComptimeNumber::Integral(ComptimeInt::BigInt { bits: bigint, ty })
}
intern::Key::NegativeInt { bigint } => {
ComptimeNumber::Integral(ComptimeInt::BigInt { bits: bigint, ty })
}
_ => {
unreachable!()
}
}
}
intern::Key::SimpleType { ty } => match ty {
intern::SimpleType::F32 => match ip.get_key(val) {
intern::Key::F32 { bits } => {
ComptimeNumber::Floating(ComptimeFloat::Binary32(bits))
}
_ => {
unreachable!()
}
},
intern::SimpleType::F64 => match ip.get_key(val) {
intern::Key::F64 { bits } => {
ComptimeNumber::Floating(ComptimeFloat::Binary64(bits))
}
_ => {
unreachable!()
}
},
intern::SimpleType::Bool => match ip.get_key(val) {
intern::Key::TrueValue => ComptimeNumber::Bool(true),
intern::Key::FalseValue => ComptimeNumber::Bool(false),
_ => unreachable!(),
},
intern::SimpleType::Void => todo!(),
intern::SimpleType::USize | intern::SimpleType::ISize => {
let ty = IntegralType::new(
matches!(
ty_key,
intern::Key::SimpleType {
ty: intern::SimpleType::ISize
}
),
pointer_bits,
);
match ip.get_key(val) {
intern::Key::SIntSmall { bits } => {
ComptimeNumber::Integral(ComptimeInt::Native {
bits: bits as _,
ty,
})
}
intern::Key::UIntSmall { bits } => {
ComptimeNumber::Integral(ComptimeInt::Native {
bits: bits as _,
ty,
})
}
intern::Key::SInt64 { bits } => ComptimeNumber::Integral(ComptimeInt::Native {
bits: bits as _,
ty,
}),
intern::Key::UInt64 { bits } => ComptimeNumber::Integral(ComptimeInt::Native {
bits: bits as _,
ty,
}),
intern::Key::PositiveInt { bigint } => {
ComptimeNumber::Integral(ComptimeInt::BigInt { bits: bigint, ty })
}
intern::Key::NegativeInt { bigint } => {
ComptimeNumber::Integral(ComptimeInt::BigInt { bits: bigint, ty })
}
_ => {
unreachable!()
}
}
}
intern::SimpleType::ComptimeInt => {
let bigint = match ip.get_key(val) {
intern::Key::SIntSmall { bits } => {
BigInt::from_signed_bytes_le(&bits.to_le_bytes())
}
intern::Key::UIntSmall { bits } => {
BigInt::from_signed_bytes_le(&bits.to_le_bytes())
}
intern::Key::SInt64 { bits } => {
BigInt::from_signed_bytes_le(&bits.to_le_bytes())
}
intern::Key::UInt64 { bits } => {
BigInt::from_signed_bytes_le(&bits.to_le_bytes())
}
intern::Key::PositiveInt { bigint } | intern::Key::NegativeInt { bigint } => {
bigint
}
_ => {
unreachable!()
}
};
ComptimeNumber::Integral(ComptimeInt::Comptime(bigint))
}
},
_ => {
unreachable!()
}
}
}
use visitor::AstVisitor;
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,
}
}
}
pub mod visitor {
use super::*;
pub trait AstExt {
fn get_node_children(&self, node: Index) -> Vec<Index>;
fn get_node_tag_and_data(&self, node: Index) -> (Tag, Data);
}
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),
}
}
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<'a> {
node: Index,
ast: &'a Ast,
ip: &'a InternPool,
}
impl<'a> Display for NodeDisplay<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let node = self.node;
let tag = self.ast.tags[node];
let loc = self.ast.source_locs[node];
let children = Children(self.ast.get_node_children(node));
let ty = self
.ast
.get_type_of_node(self.ip, &mut TypeCache::new(), node);
let is_comptime = self
.ast
.is_node_comptime_evaluable(&mut ComptimeCache::default(), node);
writeln!(
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(),
}
}
fn render<W: core::fmt::Write>(&mut self, w: &mut W) -> core::fmt::Result {
self.ast.visitor().visit_pre(|ast, scopes, node, tag, _| {
let loc = ast.source_locs[node];
let children = Children(ast.get_node_children(node));
let ty = self.ast.get_type_of_node(self.ip, &mut self.cache, node);
let is_comptime = ast.is_node_comptime_evaluable(&mut self.comptime_cache, node);
_ = writeln_indented!(
scopes.len() as u32 * 2,
w,
"{node} {}({ty}) = ({loc}) {tag:?} {children}",
if is_comptime { "CONST " } else { "" }
);
});
Ok(())
}
}
pub mod ast_gen {
use intern::{PointerFlags, SimpleType};
use itertools::Itertools;
use num_bigint::{BigInt, BigUint};
use crate::{
common::from_lo_hi_dwords,
comptime,
lexer::{Radix, TokenItem, TokenIterator},
symbol_table::syms2::SymbolKind,
tokens::PRECEDENCE_MAP,
variant,
};
use super::*;
#[derive(Debug)]
pub struct ErrorInfo {
error: ParseError,
loc: SourceLocation,
}
#[derive(Debug)]
pub struct Parser {
pub ast: Ast,
pub intern: intern::InternPool,
pub syms: crate::symbol_table::syms2::Symbols,
scopes: Vec<Index>,
pub errors: Vec<ErrorInfo>,
}
type ParseResult<T> = core::result::Result<T, ErrorInfo>;
impl Display for Parser {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.display().render(f)
}
}
impl Parser {
pub fn new() -> Parser {
Self {
ast: Ast::new(),
intern: intern::InternPool::new(),
syms: crate::symbol_table::syms2::Symbols::new(),
scopes: Vec::new(),
errors: Vec::new(),
}
}
pub fn display(&self) -> AstRenderer<'_> {
AstRenderer::new(&self.ast, &self.intern, &self.syms)
}
pub fn node_display(&self, node: Index) -> NodeDisplay<'_> {
NodeDisplay {
node,
ast: &self.ast,
ip: &self.intern,
}
}
pub fn create_comptime_folding_graph(&mut self, pointer_bits: u16) {
let mut type_cache = TypeCache::new();
let mut cache = ComptimeCache::default();
let mut nodes = self.ast.get_root_file_indices().collect::<Vec<_>>();
while let Some(node) = nodes.pop() {
if !self.ast.is_node_comptime_evaluable(&mut cache, node) {
nodes.extend(self.ast.get_node_children(node));
}
}
let mut node_map = Vec::<Index>::new();
let edges = cache
.inner
.iter()
.filter(|(_, b)| **b)
.map(|(e, _)| self.ast.get_node_children(*e).into_iter().map(|d| (*e, d)))
.flatten()
// .map(|(a, b)| (a.into_u32(), b.into_u32()))
.map(|(a, b)| {
(
node_map.iter().position(|&i| i == a).unwrap_or_else(|| {
node_map.push(a);
node_map.len() - 1
}) as u32,
node_map.iter().position(|&i| i == b).unwrap_or_else(|| {
node_map.push(b);
node_map.len() - 1
}) as u32,
)
})
.collect::<Vec<_>>();
let extra_nodes = cache
.inner
.iter()
.filter(|(_, b)| **b)
.filter_map(|(i, _)| (!node_map.contains(i)).then(|| node_map.push(*i)))
.count();
eprintln!("cache: {cache:?}");
eprintln!("node_map: {node_map:?}");
eprintln!("edges: {edges:?}");
let mut graph = petgraph::stable_graph::StableDiGraph::<(), ()>::from_edges(edges);
for _ in 0..extra_nodes {
graph.add_node(());
}
std::fs::write(
"comptime_graph.dot",
&format!(
"{:?}",
petgraph::dot::Dot::with_attr_getters(
&graph,
&[],
&|_graph, _edgeref| { "".to_string() },
&|_graph, noderef| {
format!("label = \"{}\"", node_map[noderef.0.index()])
}
)
),
)
.expect("writing comptime graph repr");
while let Some(external) = graph.externals(petgraph::Direction::Outgoing).next() {
let node = node_map[external.index()];
if !(self.ast.tags[node] == Tag::Constant || self.ast.tags[node].is_type())
&& self.ast.tags[node].is_expr()
{
eprintln!("folding {node}:\n{}", self.node_display(node));
let value = self
.ast
.comptime_value_of_node(&self.intern, pointer_bits, &mut type_cache, node)
.expect(&format!("{node} has value of None?"));
let (value, ty) = comptime_number_to_interned_type_and_value(
&mut self.intern,
pointer_bits,
value,
);
let ty = self.ast.push_interend_type(ty, self.ast.get_loc(node));
self.ast.set_tag_data_source_loc(
node,
Tag::Constant,
Data::index_and_intern(ty, value),
self.ast.get_loc(node),
);
} else {
eprintln!("rejecting {node}:\n{}", self.node_display(node));
}
// comptime fold node
graph.remove_node(external);
}
}
/// folds more AST-patterns into structures that are easier to build the IR with
pub fn fold_more_patterns(&mut self) {
use visitor::AstExt;
self.ast.visitor_mut().visit_post(|ast, _, _i, tag, data| {
match tag {
// normalise functions with block-with-trailing-expr into block
Tag::FunctionDecl => {
let (_, block) = data.as_two_indices();
let (block_tag, block_data) = ast.get_node_tag_and_data(block);
if block_tag == Tag::BlockTrailingExpr {
let (_, end) = block_data.as_extra_range();
let end = end - 1;
let expr = Index::from_u32(ast.extra[end]).unwrap();
let loc = ast.get_loc(expr);
let ret = ast.push_ret(Some(expr), loc);
// modify last element in place to be a return instruction
ast.extra[end] = ret.as_u32();
ast.tags[block] = Tag::Block;
eprintln!("folding ({block}): {block_tag:?} into Tag::Block");
eprintln!("expr: {expr:?}");
}
}
_ => {}
}
});
}
pub fn intern_types(&mut self) {
self.ast.visitor_mut().visit_post(|ast, _, i, tag, data| {
match tag {
Tag::ArrayType => {
let (length, pointee) = data.as_two_indices();
let pointee =
ast.datas[pointee].as_intern();
variant!( self.intern.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.intern.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.intern.get_array_type(
pointee,
Some(flags),
length,
);
ast.tags[i] = Tag::InternedType;
ast.datas[i] = Data::intern(ty);
}
Tag::PointerType => {
let (pointee, flags) =
data.as_index_and_extra_offset();
let pointee =
ast.datas[pointee].as_intern();
let ty = self.intern.get_pointer_type(
pointee,
Some(PointerFlags::unpack(flags as u8)),
);
ast.tags[i] = Tag::InternedType;
ast.datas[i] = Data::intern(ty);
}
Tag::TypeDeclRef => {
let decl = data.as_index();
let (name, _) = ast.datas[decl]
.as_intern_and_extra_offset();
let ty =
self.intern.get_struct_type(name, decl);
ast.tags[i] = Tag::InternedType;
ast.datas[i] = Data::intern(ty);
}
Tag::FunctionProto => {
let (_, i) = data.as_intern_and_extra_offset();
let (return_type, parameter_list) = (
Index::from_u32(ast.extra[i]).unwrap(),
Index::from_u32(ast.extra[i + 1]).unwrap(),
);
let return_type = ast.get_type_of_node(
&self.intern,
&mut TypeCache::new(),
return_type
);
let parameters = {
let (a, b) = ast.datas
[parameter_list]
.as_extra_range();
ast.extra[a..b].iter()
.map(|&i| Index::from_u32(i).unwrap())
.map(|i| {
// i is index to a parameter, a parameter is (index, intern)
let ty = ast.datas[i]
.as_index_intern()
.0;
ast.datas[ty].as_intern()
})
};
self.intern
.get_function_type(return_type, parameters);
}
Tag::StructDecl => {
let (name, offset) =
data.as_intern_and_extra_offset();
let flags =
StructFlags::unpack(ast.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 types = ast.extra[types]
.iter()
.map(|&i| Index::from_u32(i).unwrap())
.map(|i| {
ast.datas[i].as_intern()
});
let names = ast.extra[names]
.iter()
.map(|&i| intern::Index::from_u32(i));
self.intern.insert_or_replace_struct_type(
name,
i,
flags.packed,
flags.c_like,
names.zip(types),
);
}
_ => {}
}
});
}
pub fn resolve_decl_refs(&mut self) {
self.ast
.visitor_rev_mut()
.visit_post(|ast, _, node, tag, _| {
match tag {
Tag::TypeDeclRefUnresolved => {
let (scope, name) = ast.datas[node].as_index_intern();
// look in my_scope
if let Some(decl) =
self.syms
.find_type_symbol(scope, name, ast.source_locs[node])
{
ast.resolve_type_ref(node, decl)
};
}
Tag::DeclRefUnresolved => {
let (scope, name) = ast.datas[node].as_index_intern();
// look in my_scope
if let Some(decl) =
self.syms.find_symbol(scope, name, ast.source_locs[node])
{
ast.resolve_decl_ref(node, decl)
};
}
_ => {}
}
});
}
fn current_scope(&self) -> Index {
self.scopes.last().cloned().unwrap()
}
fn parse_ident(&mut self, tokens: &mut TokenIterator) -> Result<intern::Index, ErrorInfo> {
let ident = tokens.expect_token(Token::Ident).map_err(|_| ErrorInfo {
error: ParseError::ExpectedIdent,
loc: tokens.current_source_location(),
})?;
let name = self.intern.get_or_insert(intern::Key::String {
str: ident.lexeme(),
});
Ok(name)
}
fn parse_pointer(&mut self, tokens: &mut TokenIterator) -> ParseResult<Index> {
let loc = tokens.current_source_location();
tokens.eat_token(Token::Star).ok_or(ErrorInfo {
error: ParseError::ExpectedToken(Token::Star),
loc: tokens.current_source_location(),
})?;
let &[cnst, vol, noalias] =
&tokens.eat_all_zero_or_once(&[Token::Const, Token::Volatile, Token::Noalias])[..3]
else {
unreachable!()
};
let pointee = self.parse_type(tokens)?;
Ok(self
.ast
.push_pointer_type(pointee, PointerFlags::new(cnst, vol, noalias), loc))
}
/// [LENGTH]const? volatile? noalias? TYPE
fn parse_array_type(&mut self, tokens: &mut TokenIterator) -> ParseResult<Index> {
let loc = tokens.current_source_location();
let length_expr = self.parse_bracketed(tokens, |this, tokens| {
this.parse_value_expr(tokens)
// let next = tokens.peek_token().ok_or(ErrorInfo {
// error: ParseError::UnexpectedEndOfTokens,
// loc: tokens.current_source_location(),
// })?;
// match next.token() {
// Token::IntegerBinConstant
// | Token::IntegerHexConstant
// | Token::IntegerOctConstant
// | Token::IntegerConstant => {
// _ = tokens.next();
// Ok(this.parse_integral_constant(&next, next.source_location()))
// }
// _ => Err(ErrorInfo {
// error: ParseError::ExpectedConstantLiteral,
// loc: tokens.current_source_location(),
// }),
// }
})?;
let &[cnst, vol, noalias] =
&tokens.eat_all_zero_or_once(&[Token::Const, Token::Volatile, Token::Noalias])[..3]
else {
unreachable!()
};
let pointee = self.parse_type(tokens)?;
let pointer =
self.ast
.push_pointer_type(pointee, PointerFlags::new(cnst, vol, noalias), loc);
Ok(self.ast.push_array_type(length_expr, pointer, loc))
}
fn parse_simple_type(&mut self, token: Token) -> Option<intern::Index> {
match token {
Token::Void => Some(self.intern.get_assume_present(&intern::Key::SimpleType {
ty: SimpleType::Void,
})),
Token::Bool => Some(self.intern.get_assume_present(&intern::Key::SimpleType {
ty: SimpleType::Bool,
})),
Token::F32 => Some(self.intern.get_assume_present(&intern::Key::SimpleType {
ty: SimpleType::F32,
})),
Token::F64 => Some(self.intern.get_assume_present(&intern::Key::SimpleType {
ty: SimpleType::F64,
})),
Token::USize => Some(self.intern.get_assume_present(&intern::Key::SimpleType {
ty: SimpleType::USize,
})),
Token::ISize => Some(self.intern.get_assume_present(&intern::Key::SimpleType {
ty: SimpleType::ISize,
})),
_ => None,
}
}
fn try_parse_integral_type(
&mut self,
typename: &str,
) -> Result<Option<intern::Index>, ParseError> {
let mut iter = typename.chars().peekable();
let signed = match iter.next() {
Some('u') => false,
Some('i') => true,
_ => {
return Ok(None);
}
};
// need 1 digit for an integral type
if iter.peek().map(|&c| crate::common::is_digit(c)) != Some(true) {
return Ok(None);
}
// need no nondigits after digits
if iter
.clone()
.skip_while(|&c| crate::common::is_digit(c))
.next()
.is_some()
{
return Ok(None);
}
let mut bits = 0u16;
loop {
let Some(digit) = iter.next().map(|c| c as u8 - b'0') else {
break;
};
match bits
.checked_mul(10)
.and_then(|bits| bits.checked_add(digit as u16))
{
Some(val) => {
bits = val;
}
None => {
// this IS an integral type, but it is bigger than u/i65535
return Err(ParseError::IntegralTypeTooWide);
}
}
}
Ok(Some(self.intern.get_int_type(signed, bits)))
}
fn parse_integral_constant_inner(
&mut self,
item: &TokenItem,
) -> (intern::Index, intern::Index) {
let radix = Radix::from_token(item.token()).unwrap();
let mut chars = item.lexeme().char_indices();
match radix {
Radix::Dec => {}
_ => {
_ = chars.advance_by(2);
}
}
let digits = chars
.take_while_ref(|&(_, c)| radix.is_digit()(c) || c == '_')
.filter(|&(_, c)| c != '_')
.map(|(_, c)| c)
.collect::<Vec<_>>();
let value = comptime::bigint::parse_bigint(digits.into_iter(), radix);
let ty = match chars.clone().next() {
Some((i, 'u')) | Some((i, 'i')) => self
.try_parse_integral_type(&item.lexeme()[i..])
.expect("invalid integral type??"),
_ => None,
};
let interned = match value.len() {
..1 => {
let bits = value.get(0).cloned().unwrap_or(0);
self.intern.get_or_insert(intern::Key::UIntSmall { bits })
}
..2 => {
let lo = value.get(0).cloned().unwrap_or(0);
let hi = value.get(1).cloned().unwrap_or(0);
let bits = from_lo_hi_dwords(lo, hi);
self.intern.get_or_insert(intern::Key::UInt64 { bits })
}
_ => {
let bigint = BigInt::from_biguint(num_bigint::Sign::Plus, BigUint::new(value));
self.intern
.get_or_insert(intern::Key::PositiveInt { bigint })
}
};
let ty = ty.unwrap_or(self.intern.get_comptime_int_type());
(interned, ty)
}
fn parse_integral_constant(&mut self, item: &TokenItem, loc: SourceLocation) -> Index {
let (interned, ty) = self.parse_integral_constant_inner(item);
let ty = self.ast.push_interend_type(ty, loc);
return self.ast.push_constant(interned, ty, loc);
}
fn parse_floating_constant(&mut self, item: &TokenItem, loc: SourceLocation) -> Index {
let lexeme = item.lexeme();
let lexeme = lexeme
.strip_suffix("f32")
.map(|l| (l, self.intern.get_f32_type()))
.unwrap_or(
lexeme
.strip_suffix("f64")
.map(|l| (l, self.intern.get_f64_type()))
.unwrap_or((lexeme, self.intern.get_f64_type())),
);
let bits = if lexeme.1 == self.intern.get_f32_type() {
self.intern.get_or_insert(intern::Key::F32 {
bits: lexeme.0.parse::<f32>().unwrap(),
})
} else {
self.intern.get_or_insert(intern::Key::F64 {
bits: lexeme.0.parse::<f64>().unwrap(),
})
};
let ty = self.ast.push_interend_type(lexeme.1, loc);
return self.ast.push_constant(bits, ty, loc);
}
/// TYPE <-
/// * TYPE
/// IDENTIFIER
/// SIMPLE_TYPE
/// [ TYPE ; CONSTANT_EXPR ]
/// INTEGRAL_TYPE // u[0..65535] | i[0..65535]
fn parse_type(&mut self, tokens: &mut TokenIterator) -> ParseResult<Index> {
let loc = tokens.current_source_location();
match tokens
.peek_token()
.ok_or(ErrorInfo {
error: ParseError::ExpectedTypeName,
loc: tokens.current_source_location(),
})?
.token()
{
Token::Star => self.parse_pointer(tokens),
Token::OpenSquareBracket => self.parse_array_type(tokens),
Token::Ident => {
let token = tokens.next().unwrap();
match self
.try_parse_integral_type(token.lexeme())
.map_err(|error| ErrorInfo {
error,
loc: token.source_location(),
})? {
Some(int) => Ok(self.ast.push_interend_type(int, loc)),
None => {
let name = self.intern.get_or_insert(intern::Key::String {
str: token.lexeme(),
});
// TODO: this will cause issues with redefinitions of types with the same name
// and actually, make type into a proper node of the ast
Ok(self
.ast
.push_type_ref_unresolved(self.current_scope(), name, loc))
}
}
}
token => {
let ty = self.parse_simple_type(token).ok_or(ErrorInfo {
error: ParseError::ExpectedTypeName,
loc: tokens.current_source_location(),
})?;
_ = tokens.next();
Ok(self.ast.push_interend_type(ty, loc))
}
}
}
/// GLOBAL_DECL <-
/// const IDENTIFIER: TYPENAME = EXPR;
fn parse_const_decl(&mut self, tokens: &mut TokenIterator) -> ParseResult<Index> {
let err = 'blk: {
let loc = tokens.current_source_location();
let Some(_) = tokens.eat_token(Token::Const) else {
break 'blk ErrorInfo {
error: ParseError::ExpectedToken(Token::Const),
loc,
};
};
let ident = match self.parse_ident(tokens) {
Ok(i) => i,
Err(err) => {
break 'blk err;
}
};
let Some(_) = tokens.eat_token(Token::Colon) else {
return Err(ErrorInfo {
error: ParseError::ExpectedToken(Token::Colon),
loc,
});
};
let typename = match self.parse_type(tokens) {
Ok(i) => i,
Err(err) => {
break 'blk err;
}
};
let Some(_) = tokens.eat_token(Token::Equal) else {
break 'blk ErrorInfo {
error: ParseError::ExpectedToken(Token::Equal),
loc: tokens.current_source_location(),
};
};
let expr = match self.parse_value_expr(tokens) {
Ok(i) => i,
Err(err) => {
break 'blk err;
}
};
let Some(_) = tokens.eat_token(Token::Semi) else {
break 'blk ErrorInfo {
error: ParseError::ExpectedToken(Token::Semi),
loc: tokens.current_source_location(),
};
};
let decl = self.ast.push_global_decl(ident, typename, expr, loc);
self.syms
.insert_symbol(self.current_scope(), ident, SymbolKind::Const, decl);
return Ok(decl);
};
tokens.advance_past_semi().ok_or(ErrorInfo {
error: ParseError::ExpectedToken(Token::Semi),
loc: tokens.current_source_location(),
})?;
Ok(self.ast.push_error(err.error, err.loc))
}
/// FUNCTION_PROTO <-
/// fn IDENTIFIER ()
/// fn IDENTIFIER () -> TYPENAME
/// fn IDENTIFIER ( PARAMETER_LIST ,? )
/// fn IDENTIFIER ( PARAMETER_LIST ,? ) -> TYPENAME
fn parse_fn_proto(
&mut self,
tokens: &mut TokenIterator,
) -> ParseResult<(Index, intern::Index)> {
let loc = tokens.current_source_location();
let _ = tokens.eat_token(Token::Fn).ok_or(ErrorInfo {
error: ParseError::ExpectedToken(Token::Fn),
loc,
})?;
let ident = self.parse_ident(tokens)?;
let parameters = self.parse_parenthesised(tokens, |this, tokens| {
if tokens.is_next_token(Token::CloseParens) {
Ok(this.ast.push_parameter_list([], loc))
} else {
this.parse_parameter_list(tokens)
}
})?;
let return_type = if let Some(_) = tokens.eat_token(Token::MinusGreater) {
self.parse_type(tokens)?
} else {
self.ast.push_interend_type(
self.intern.get_void_type(),
tokens.current_source_location(),
)
};
let decl = self.ast.push_fn_proto(ident, return_type, parameters, loc);
Ok((decl, ident))
}
fn parse_fn_inner(&mut self, tokens: &mut TokenIterator) -> ParseResult<Index> {
let loc = tokens.current_source_location();
let func = self.ast.reserve_node(Kind::Function);
self.push_scope(func, intern::Index::invalid());
let (proto, ident) = self.parse_fn_proto(tokens).map_err(|e| {
self.pop_scope();
e
})?;
let body = self
.parse_block(tokens)
.map(|pv| self.convert_to_value_expr(pv))
.map_err(|e| {
self.pop_scope();
e
})?;
self.pop_scope();
self.ast.set_fn_decl(func, proto, body, loc);
self.syms
.insert_symbol(self.current_scope(), ident, SymbolKind::Function, func);
Ok(func)
}
/// FUNCTION_DECL <-
/// FUNCTION_PROTO BLOCK
fn parse_fn_decl(&mut self, tokens: &mut TokenIterator) -> Index {
match self.parse_fn_inner(tokens) {
Ok(i) => i,
Err(err) => {
self.find_next_fn_or_const(tokens);
self.push_error(err.error, err.loc)
}
}
}
/// RETURN_STATEMENT <-
/// return EXPRESSION? ;
fn parse_return_stmt(&mut self, tokens: &mut TokenIterator) -> ParseResult<Index> {
// SAFETY: function invariance
let ret = tokens.next().unwrap();
let loc = ret.source_location();
let expr = if tokens.eat_token(Token::Semi).is_some() {
self.ast.push_ret(None, loc)
} else {
match self.parse_value_expr(tokens) {
Ok(i) => {
tokens.eat_token(Token::Semi).ok_or(ErrorInfo {
error: ParseError::ExpectedToken(Token::Semi),
loc: tokens.current_source_location(),
})?;
self.ast.push_ret(Some(i), loc)
}
Err(err) => {
tokens.advance_past_semi().ok_or(ErrorInfo {
error: ParseError::ExpectedToken(Token::Semi),
loc: tokens.current_source_location(),
})?;
self.push_error(err.error, err.loc)
}
}
};
Ok(expr)
}
/// VAR_DECL <-
/// (let | var) IDENTIFIER (: TYPENAME)? ;
/// (let | var) IDENTIFIER (: TYPENAME)? = EXPRESSION ;
fn parse_var_decl(&mut self, tokens: &mut TokenIterator) -> ParseResult<Index> {
match self.parse_var_decl_inner(tokens) {
Ok(i) => {
_ = tokens.eat_token(Token::Semi).ok_or(ErrorInfo {
error: ParseError::ExpectedToken(Token::Semi),
loc: tokens.current_source_location(),
})?;
Ok(i)
}
Err(err) => {
tokens.advance_past_semi().ok_or(ErrorInfo {
error: ParseError::ExpectedToken(Token::Semi),
loc: tokens.current_source_location(),
})?;
Ok(self.push_error(err.error, err.loc))
}
}
}
fn parse_var_decl_inner(&mut self, tokens: &mut TokenIterator) -> ParseResult<Index> {
// SAFETY: function invariance
let let_or_var = tokens.next().unwrap();
let loc = let_or_var.source_location();
let is_let = let_or_var.token() == Token::Let;
let name = self.parse_ident(tokens)?;
let ty = if tokens.eat_token(Token::Colon).is_some() {
Some(self.parse_type(tokens)?)
} else {
None
};
let assignment = if tokens.eat_token(Token::Equal).is_some() {
Some(self.parse_value_expr(tokens)?)
} else {
None
};
let decl = self.ast.push_var_decl(is_let, name, ty, assignment, loc);
self.syms.insert_symbol(
self.current_scope(),
name,
SymbolKind::Local(tokens.current_source_location()),
decl,
);
Ok(decl)
}
fn parse_block_inner(
&mut self,
block: Index,
tokens: &mut TokenIterator,
) -> ParseResult<PlaceOrValue> {
let loc = tokens.current_source_location();
let mut statements = Vec::new();
let trailing = loop {
if tokens.is_next_token(Token::CloseBrace) {
break None;
}
let next = tokens.peek_token().ok_or(ErrorInfo {
error: ParseError::UnexpectedEndOfTokens,
loc: tokens.current_source_location(),
})?;
if let Some(decl) = self.parse_constant_decls(tokens)? {
statements.push(decl);
} else {
match next.token() {
Token::Return => {
statements.push(self.parse_return_stmt(tokens)?);
}
Token::Var | Token::Let => {
statements.push(self.parse_var_decl(tokens)?);
}
_ => {
if self.is_statement(tokens) {
// expr -> statements
let expr = self
.parse_with_trailing_semi(tokens, |this, tokens| {
Ok(this.parse_expr(tokens)?.into_index())
})?;
statements.push(expr);
} else {
// expr -> trailing
let expr = self.parse_expr(tokens)?;
if !tokens.is_next_token(Token::CloseBrace) {
statements.push(self.push_error(
ParseError::ExpectedEndOfBlock,
tokens.current_source_location(),
));
} else {
break Some(expr);
}
}
}
}
}
};
self.ast.set_block(
block,
statements,
trailing.map(PlaceOrValue::into_index),
loc,
);
let block = trailing
.map(|pv| pv.with_index(block))
.unwrap_or(PlaceOrValue::Value(block));
Ok(block)
}
/// BLOCK <-
/// { STATEMENT* EXPRESSION? }
fn parse_block(&mut self, tokens: &mut TokenIterator) -> ParseResult<PlaceOrValue> {
let block = self.parse_braced(tokens, |this, tokens| {
let block = this.ast.reserve_node_other();
this.push_scope(block, intern::Index::invalid());
let block_result = this.parse_block_inner(block, tokens);
this.pop_scope();
block_result
})?;
Ok(block)
}
/// PARAMETER_LIST <-
/// PARAMETER
/// PARAMETER_LIST , ARGUMENT
fn parse_parameter_list(&mut self, tokens: &mut TokenIterator) -> ParseResult<Index> {
let loc = tokens.current_source_location();
let mut params = Vec::new();
loop {
params.push(self.parse_parameter(tokens)?);
if !tokens.is_next_token(Token::Comma) {
break;
}
if tokens.is_next_token2(Token::CloseParens) {
break;
}
// skip comma
_ = tokens.next();
}
return Ok(self.ast.push_parameter_list(params, loc));
}
/// PARAMETER <-
/// IDENT : TYPENAME
fn parse_parameter(&mut self, tokens: &mut TokenIterator) -> ParseResult<Index> {
let loc = tokens.current_source_location();
let name = self.parse_ident(tokens)?;
let Some(_) = tokens.eat_token(Token::Colon) else {
return Err(ErrorInfo {
error: ParseError::ExpectedToken(Token::Colon),
loc,
});
};
let ty = self.parse_type(tokens)?;
let param = self.ast.push_parameter(name, ty, loc);
self.syms
.insert_symbol(self.current_scope(), name, SymbolKind::Local(loc), param);
return Ok(param);
}
/// ARGUMENT <-
/// IDENT : EXPR
/// EXPR
fn parse_argument(&mut self, tokens: &mut TokenIterator) -> ParseResult<Index> {
let loc = tokens.current_source_location();
let name = if tokens.is_next_token2(Token::Colon) && tokens.is_next_token(Token::Ident)
{
let name = self.parse_ident(tokens)?;
// we checked `is_next_token2`
_ = tokens.eat_token(Token::Colon).unwrap();
Some(name)
} else {
None
};
let expr = self.parse_value_expr(tokens)?;
let i = match name {
Some(name) => self.ast.push_named_argument(name, expr, loc),
None => self.ast.push_argument(expr, loc),
};
Ok(i)
}
/// ARGUMENT_LIST <-
/// ARGUMENT
/// ARGUMENT_LIST , ARGUMENT
fn parse_argument_list(&mut self, tokens: &mut TokenIterator) -> ParseResult<Index> {
let loc = tokens.current_source_location();
let mut args = Vec::new();
loop {
args.push(self.parse_argument(tokens)?);
if !tokens.is_next_token(Token::Comma) {
break;
}
if tokens.is_next_token2(Token::CloseParens) {
break;
}
// skip comma
_ = tokens.next();
}
return Ok(self.ast.push_argument_list(args, loc));
}
/// PRIMARY_EXPR <-
/// IDENTIFIER
/// INTEGER_CONSTANT
/// FLOATING_CONSTANT
/// ( EXPRESSION )
/// BLOCK
fn parse_primary_expr(&mut self, tokens: &mut TokenIterator) -> ParseResult<PlaceOrValue> {
let loc = tokens.current_source_location();
let Some(next) = tokens.peek_token() else {
return Err(ErrorInfo {
error: ParseError::ExpectedPrimaryExpression,
loc,
});
};
match next.token() {
Token::IntegerBinConstant
| Token::IntegerHexConstant
| Token::IntegerOctConstant
| Token::IntegerConstant => {
_ = tokens.next();
return Ok(PlaceOrValue::Value(
self.parse_integral_constant(&next, next.source_location()),
));
}
Token::FloatingConstant
| Token::FloatingExpConstant
| Token::DotFloatingConstant
| Token::DotFloatingExpConstant => {
_ = tokens.next();
return Ok(PlaceOrValue::Value(
self.parse_floating_constant(&next, next.source_location()),
));
}
Token::OpenParens => {
let expr =
self.parse_parenthesised(tokens, |this, tokens| this.parse_expr(tokens))?;
return Ok(expr);
}
Token::OpenBrace => {
return Ok(self.parse_block(tokens)?);
}
Token::Ident => {
_ = tokens.next();
let ident = next.lexeme();
let ident = self
.intern
.get_or_insert(intern::Key::String { str: ident });
return Ok(PlaceOrValue::Place(self.ast.push_decl_ref_unresolved(
self.current_scope(),
ident,
loc,
)));
}
// TODO: eventually handle paths
_ => {
return Err(ErrorInfo {
error: ParseError::ExpectedPrimaryExpression,
loc,
});
}
}
}
/// POSTFIX_EXPR <-
/// PRIMARY_EXPR
/// PRIMARY_EXPR ( )
/// PRIMARY_EXPR ( ARGUMENT_LIST )
/// PRIMARY_EXPR [ EXPR ]
/// POSTFIX_EXPR . IDENTIFIER
fn parse_postfix_expr(&mut self, tokens: &mut TokenIterator) -> ParseResult<PlaceOrValue> {
let mut lhs = self.parse_primary_expr(tokens)?;
while let Some(postfix) = self.try_parse_postfix_expr_inner(tokens, lhs)? {
lhs = postfix;
}
Ok(lhs)
}
fn try_parse_postfix_expr_inner(
&mut self,
tokens: &mut TokenIterator,
lhs: PlaceOrValue,
) -> ParseResult<Option<PlaceOrValue>> {
let lhs = if let Some(next) = tokens.peek_token() {
let loc = next.source_location();
match next.token() {
Token::OpenParens => {
let arguments = self.parse_parenthesised(tokens, |this, tokens| {
if tokens.is_next_token(Token::CloseParens) {
Ok(this.ast.push_argument_list([], loc))
} else {
this.parse_argument_list(tokens)
}
})?;
let lhs = self.convert_to_value_expr(lhs);
Some(PlaceOrValue::Value(
self.ast.push_call_expr(lhs, arguments, loc),
))
}
Token::OpenSquareBracket => {
let subscript = self.parse_bracketed(tokens, |this, tokens| {
this.parse_value_expr(tokens)
})?;
let lhs = self.convert_to_value_expr(lhs);
Some(PlaceOrValue::Place(self.ast.push_binary(
Tag::SubscriptExpr,
lhs,
subscript,
loc,
)))
}
Token::Dot if tokens.is_next_token2(Token::Ident) => {
_ = tokens.next();
let loc = tokens.current_source_location();
let lhs = self.convert_to_place_expr(lhs);
let name = self.parse_ident(tokens)?;
Some(self.ast.push_field_access(lhs, name, loc))
}
_ => None,
}
} else {
None
};
Ok(lhs)
}
fn push_error(&mut self, error: ParseError, loc: SourceLocation) -> Index {
self.errors.push(ErrorInfo { error, loc });
self.ast.push_error(error, loc)
}
/// converts the expression to a value expression, if it isn't one already.
fn convert_to_value_expr(&mut self, lrvalue: PlaceOrValue) -> Index {
match lrvalue {
PlaceOrValue::Value(index) => index,
PlaceOrValue::Place(index) => self.ast.push_place_to_value_conversion(index),
}
}
/// converts the expression to a place expression, if it isn't one already.
fn convert_to_place_expr(&mut self, lrvalue: PlaceOrValue) -> Index {
match lrvalue {
PlaceOrValue::Place(index) => index,
PlaceOrValue::Value(index) => self.ast.push_value_to_place_conversion(index),
}
}
/// PREFIX_EXPR <-
/// POSTFIX_EXPR
/// ! POSTFIX_EXPR
/// - POSTFIX_EXPR
/// & POSTFIX_EXPR
/// * POSTFIX_EXPR
fn parse_prefix_expr(&mut self, tokens: &mut TokenIterator) -> ParseResult<PlaceOrValue> {
let next = tokens.peek_token().ok_or(ErrorInfo {
error: ParseError::ExpectedPrefixExpression,
loc: tokens.current_source_location(),
})?;
let loc = next.source_location();
let expr = match next.token() {
Token::Bang => {
_ = tokens.next();
let lhs = self.parse_prefix_expr(tokens)?;
let lhs = self.convert_to_value_expr(lhs);
self.ast.push_unary(Tag::Not, lhs, loc)
}
Token::Minus => {
_ = tokens.next();
let lhs = self.parse_prefix_expr(tokens)?;
let lhs = self.convert_to_value_expr(lhs);
self.ast.push_unary(Tag::Negate, lhs, loc)
}
Token::Ampersand => {
_ = tokens.next();
// the address-of operator requires lhs to be a place
// expression. not all expressions that might be the lhs
// are automatically place expressions: the construct `let a
// = &3;` has `3`, which is a value expression, as the lhs
// of the address-of operator.
let lhs = self.parse_prefix_expr(tokens)?;
let lhs = self.convert_to_place_expr(lhs);
self.ast.push_unary(Tag::AddressOf, lhs, loc)
}
Token::Star => {
_ = tokens.next();
let lhs = self.parse_prefix_expr(tokens)?;
self.ast.push_unary(Tag::Deref, lhs.into_index(), loc)
}
_ => self.parse_postfix_expr(tokens)?,
};
Ok(expr)
}
/// AS_EXPR <-
/// PREFIX_EXPR
/// PREFIX_EXPR as TYPENAME
fn parse_as_expr(&mut self, tokens: &mut TokenIterator) -> ParseResult<PlaceOrValue> {
let loc = tokens.current_source_location();
let expr = self.parse_prefix_expr(tokens)?;
if tokens.eat_token(Token::As).is_some() {
let typename = self.parse_type(tokens)?;
let expr = self.convert_to_value_expr(expr);
return Ok(PlaceOrValue::Value(self.ast.push_cast(expr, typename, loc)));
} else {
return Ok(expr);
}
}
/// BINARY_EXPR <-
/// AS_EXPR
/// AS_EXPR * EXPRESSION
/// AS_EXPR / EXPRESSION
/// AS_EXPR % EXPRESSION
/// AS_EXPR + EXPRESSION
/// AS_EXPR - EXPRESSION
/// AS_EXPR << EXPRESSION
/// AS_EXPR >> EXPRESSION
/// AS_EXPR < EXPRESSION
/// AS_EXPR > EXPRESSION
/// AS_EXPR <= EXPRESSION
/// AS_EXPR >= EXPRESSION
/// AS_EXPR == EXPRESSION
/// AS_EXPR != EXPRESSION
/// AS_EXPR & EXPRESSION
/// AS_EXPR ^ EXPRESSION
/// AS_EXPR | EXPRESSION
/// AS_EXPR && EXPRESSION
/// AS_EXPR || EXPRESSION
fn parse_binary_expr(
&mut self,
tokens: &mut TokenIterator,
precedence: u32,
) -> ParseResult<PlaceOrValue> {
let mut node = self.parse_as_expr(tokens)?;
loop {
let Some(tok) = tokens.peek_token() else {
break;
};
let loc = tok.source_location();
let Some(prec) = PRECEDENCE_MAP.get(&tok.token()).cloned() else {
break;
};
if prec < precedence {
break;
}
// SAFETY: we peeked `tok`
let tok = tokens.next().unwrap();
let lhs = self.convert_to_value_expr(node);
let rhs = self.parse_binary_expr(tokens, prec + 1)?;
let rhs = self.convert_to_value_expr(rhs);
let tag = match tok.token() {
Token::PipePipe => Tag::Or,
Token::AmpersandAmpersand => Tag::And,
Token::Pipe => Tag::BitOr,
Token::Caret => Tag::BitXOr,
Token::Ampersand => Tag::BitAnd,
Token::BangEqual => Tag::NEq,
Token::EqualEqual => Tag::Eq,
Token::LessEqual => Tag::Le,
Token::GreaterEqual => Tag::Ge,
Token::Less => Tag::Lt,
Token::Greater => Tag::Gt,
Token::GreaterGreater => Tag::Shr,
Token::LessLess => Tag::Shl,
Token::Plus => Tag::Add,
Token::Minus => Tag::Sub,
Token::Percent => Tag::Rem,
Token::Star => Tag::Mul,
Token::Slash => Tag::Div,
_ => unreachable!(),
};
node = PlaceOrValue::Value(self.ast.push_binary(tag, lhs, rhs, loc));
}
Ok(node)
}
/// ASSIGNMENT_EXPR <-
/// BINARY_EXPRESSION
/// BINARY_EXPRESSION ASSIGNMENT_OP EXPRESSION
/// ASSIGNMENT_OP <-
/// = += -= *= /= %= ...
fn parse_assignment_expr(
&mut self,
tokens: &mut TokenIterator,
) -> ParseResult<PlaceOrValue> {
let lhs = self.parse_binary_expr(tokens, 0)?;
if tokens
.peek_token()
.map(|itm| itm.token().is_assignment_op())
== Some(true)
{
// SAFETY: we peeked
let op = tokens.next().unwrap();
let loc = op.source_location();
// rhs (src) must be a value
let rhs = self.parse_value_expr(tokens)?;
let rhs = if op.token() == Token::Equal {
rhs
} else {
let tag = match op.token() {
Token::PlusEqual => Tag::Add,
Token::MinusEqual => Tag::Sub,
Token::StarEqual => Tag::Mul,
Token::SlashEqual => Tag::Sub,
Token::PercentEqual => Tag::Rem,
Token::PipeEqual => Tag::BitOr,
Token::CaretEqual => Tag::BitXOr,
Token::AmpersandEqual => Tag::BitAnd,
Token::LessLessEqual => Tag::Shl,
Token::GreaterGreaterEqual => Tag::Shr,
_ => {
unreachable!()
}
};
// convert lhs (dest) to value
let lhs = self.convert_to_value_expr(lhs);
self.ast.push_binary(tag, lhs, rhs, loc)
};
// for the assignment, lhs (dest) must be a place
// I think this is the only case where the AST is a dag.
let lhs = self.convert_to_place_expr(lhs);
// the assignment is a value
Ok(PlaceOrValue::Value(self.ast.push_assign(lhs, rhs, loc)))
} else {
// but if we don't have an assignment, this might be a place
Ok(lhs)
}
}
/// ELSE_EXPR <-
/// 'else' (IF_EXPR | EXPR_OR_STATEMENT_OR_BLOCK)
fn parse_else_expr(&mut self, tokens: &mut TokenIterator) -> ParseResult<PlaceOrValue> {
// SAFETY: function invariance
let _else_ = tokens.eat_token(Token::Else).unwrap();
if tokens.is_next_token(Token::If) {
self.parse_if_expr(tokens)
} else {
self.parse_expr_or_block_as_block(tokens)
}
}
/// IF_EXPR <-
/// 'if' ( EXPR ) EXPR_OR_STATEMENT_OR_BLOCK ELSE_EXPR?
fn parse_if_expr(&mut self, tokens: &mut TokenIterator) -> ParseResult<PlaceOrValue> {
// SAFETY: function invariance
let iff = tokens.eat_token(Token::If).unwrap();
let loc = iff.source_location();
let cond =
self.parse_parenthesised(tokens, |this, tokens| this.parse_value_expr(tokens))?;
let body = self.parse_expr_or_block_as_block(tokens)?;
if tokens.is_next_token(Token::Else) {
let else_expr = self.parse_else_expr(tokens)?;
Ok(body.with_index(self.ast.push_if_else(
cond,
body.into_index(),
else_expr.into_index(),
loc,
)))
} else {
Ok(body.with_index(self.ast.push_if(cond, body.into_index(), loc)))
}
}
fn parse_expr_or_block_as_block(
&mut self,
tokens: &mut TokenIterator,
) -> ParseResult<PlaceOrValue> {
let Some(next) = tokens.peek_token() else {
return Err(ErrorInfo {
error: ParseError::ExpectedExpression,
loc: tokens.current_source_location(),
});
};
match next.token() {
Token::OpenBrace => self.parse_block(tokens),
_ => {
let loc = tokens.current_source_location();
let expr = self.parse_expr(tokens)?;
Ok(expr.with_index(self.ast.push_block([], Some(expr.into_index()), loc)))
}
}
}
fn parse_expr(&mut self, tokens: &mut TokenIterator) -> ParseResult<PlaceOrValue> {
let loc = tokens.current_source_location();
let Some(next) = tokens.peek_token() else {
return Err(ErrorInfo {
error: ParseError::ExpectedExpression,
loc,
});
};
let expr = match next.token() {
Token::If => self.parse_if_expr(tokens)?,
_ => self.parse_assignment_expr(tokens)?,
};
Ok(expr)
}
fn parse_place_expr(&mut self, tokens: &mut TokenIterator) -> ParseResult<Index> {
// TODO: panic if not place expr
let expr = self.parse_expr(tokens)?;
let expr = self.convert_to_place_expr(expr);
Ok(expr)
}
fn parse_value_expr(&mut self, tokens: &mut TokenIterator) -> ParseResult<Index> {
// TODO: convert from place to value (lvalue-to-rvalue)
let expr = self.parse_expr(tokens)?;
let expr = self.convert_to_value_expr(expr);
Ok(expr)
}
/// TYPE_DECL <-
/// type IDENTIFIER = TYPE_UNION ;
/// type IDENTIFIER = '(' (TYPE,)* ')' ;
/// type IDENTIFIER = extern? union { (IDENTIFIER: TYPE,)* }
/// type IDENTIFIER = extern? packed? enum { (IDENTIFIER (= EXPRESSION),)* }
/// type IDENTIFIER = extern? packed? struct { (IDENTIFIER: TYPE,)* }
fn parse_type_decl(&mut self, tokens: &mut TokenIterator) -> ParseResult<Index> {
_ = tokens.eat_token(Token::Type).ok_or(ErrorInfo {
error: ParseError::ExpectedToken(Token::Type),
loc: tokens.current_source_location(),
});
let name = self.parse_ident(tokens)?;
let loc = tokens.current_source_location();
_ = tokens.eat_token(Token::Equal).ok_or(ErrorInfo {
error: ParseError::ExpectedToken(Token::Equal),
loc: tokens.current_source_location(),
});
let (has_attributes, c_like, packed) = {
let vec = tokens.eat_all_zero_or_once(&[Token::Extern, Token::Packed]);
(vec[0] || vec[1], vec[0], vec[1])
};
let Some(next) = tokens.peek_token() else {
return Err(ErrorInfo {
error: ParseError::ExpectedTypeDeclaration,
loc: tokens.current_source_location(),
});
};
let decl = match next.token() {
Token::Struct => self.parse_struct_decl(tokens, name, c_like, packed, loc),
Token::Union => {
unimplemented!()
}
Token::Enum => {
unimplemented!()
}
_ => {
if has_attributes {
return Err(ErrorInfo {
error: ParseError::UnexpectedTypeAttributes,
loc: tokens.current_source_location(),
});
}
match next.token() {
Token::OpenParens => {
// tuple
unimplemented!()
}
Token::Ident => {
// sumtype
unimplemented!()
}
_ => {
return Err(ErrorInfo {
error: ParseError::ExpectedTypeDeclaration,
loc: tokens.current_source_location(),
});
}
}
}
}?;
self.syms
.insert_symbol(self.current_scope(), name, SymbolKind::Type, decl);
Ok(decl)
}
/// SUMTYPE_DECL <-
/// type IDENTIFIER = TYPE_UNION
/// TYPE_UNION <-
/// TYPE (| TYPE_UNION)?
/// IDENTIFIER: TYPE (| TYPE_UNION)?
fn parse_sumtype_decl(&mut self, _tokens: &mut TokenIterator) -> ParseResult<Index> {
todo!()
}
/// TUPLE_DECL <-
/// type IDENTIFIER = (TYPE,* )
fn parse_tuple_decl(&mut self, _tokens: &mut TokenIterator) -> ParseResult<Index> {
todo!()
}
/// UNION_DECL <-
/// type IDENTIFIER = union { IDENTIFIER: TYPE,* }
fn parse_union_decl(&mut self, _tokens: &mut TokenIterator) -> ParseResult<Index> {
todo!()
}
/// ENUM_DECL <-
/// type IDENTIFIER = packed? enum { IDENTIFIER (= EXPRESSION),* }
fn parse_enum_decl(&mut self, _tokens: &mut TokenIterator) -> ParseResult<Index> {
todo!()
}
/// STRUCT_DECL <-
/// type IDENTIFIER = extern? packed? struct { STRUCT_FIELD,* }
fn parse_struct_decl(
&mut self,
tokens: &mut TokenIterator,
name: intern::Index,
c_like: bool,
packed: bool,
loc: SourceLocation,
) -> ParseResult<Index> {
// SAFETY: function invariance
_ = tokens.eat_token(Token::Struct).ok_or(ErrorInfo {
error: ParseError::ExpectedToken(Token::Struct),
loc: tokens.current_source_location(),
})?;
let decl = self.ast.reserve_node(Kind::GlobalDecl);
let decl = self.parse_braced(tokens, |this, tokens| {
this.parse_struct_fields(tokens).map(|fields| {
_ = tokens.eat_token(Token::Comma);
let flags = StructFlags::new(packed, c_like, fields.len() as u32);
this.intern.insert_or_replace_struct_type(
name,
decl,
flags.packed,
flags.c_like,
vec![],
);
this.ast.set_struct_decl(decl, name, flags, fields, loc)
})
})?;
Ok(decl)
}
fn parse_with_trailing_semi<F, I>(
&mut self,
tokens: &mut TokenIterator,
parse: F,
) -> ParseResult<I>
where
F: FnOnce(&mut Self, &mut TokenIterator) -> ParseResult<I>,
Index: Into<I>,
{
match parse(self, tokens) {
Ok(i) => {
_ = tokens.eat_token(Token::Semi).ok_or(ErrorInfo {
error: ParseError::ExpectedToken(Token::Semi),
loc: tokens.current_source_location(),
})?;
Ok(i)
}
Err(err) => {
tokens.advance_past_semi().ok_or(ErrorInfo {
error: ParseError::ExpectedToken(Token::Semi),
loc: tokens.current_source_location(),
})?;
Ok(self.push_error(err.error, err.loc).into())
}
}
}
fn parse_inner<F, I, E>(
&mut self,
tokens: &mut TokenIterator,
open: Token,
close: Token,
parse: F,
on_err: E,
) -> ParseResult<I>
where
F: FnOnce(&mut Self, &mut TokenIterator) -> ParseResult<I>,
E: FnOnce(&mut Self, &mut TokenIterator, ErrorInfo, TokenItem) -> ParseResult<I>,
{
let Some(start) = tokens.eat_token(open) else {
return Err(ErrorInfo {
error: ParseError::ExpectedToken(open),
loc: tokens.current_source_location(),
});
};
match parse(self, tokens) {
Ok(i) => {
_ = tokens.eat_token(close).ok_or(ErrorInfo {
error: match open {
Token::OpenBrace => ParseError::UnmatchedBrace(start.token_pos().start),
Token::OpenParens => {
ParseError::UnmatchedParens(start.token_pos().start)
}
Token::OpenSquareBracket => {
ParseError::UnmatchedSquareBracket(start.token_pos().start)
}
_ => ParseError::UnmatchedDelimiter(start.token_pos().start),
},
loc: tokens.current_source_location(),
})?;
Ok(i)
}
Err(e) => on_err(self, tokens, e, start),
}
}
fn parse_inner2<F, I>(
&mut self,
tokens: &mut TokenIterator,
open: Token,
close: Token,
parse: F,
) -> ParseResult<I>
where
F: FnOnce(&mut Self, &mut TokenIterator) -> ParseResult<I>,
Index: Into<I>,
// I: From<Index>,
{
self.parse_inner(tokens, open, close, parse, |this, tokens, err, start| {
match close {
Token::CloseBrace => {
tokens.advance_past_end_of_braced().ok_or(ErrorInfo {
error: ParseError::UnmatchedBrace(start.token_pos().start),
loc: tokens.current_source_location(),
})?;
}
Token::CloseParens => {
tokens.advance_past_end_of_parens().ok_or(ErrorInfo {
error: ParseError::UnmatchedParens(start.token_pos().start),
loc: tokens.current_source_location(),
})?;
}
Token::CloseSquareBracket => {
tokens.advance_past_end_of_bracketed().ok_or(ErrorInfo {
error: ParseError::UnmatchedSquareBracket(start.token_pos().start),
loc: tokens.current_source_location(),
})?;
}
Token::Semi => {
tokens.advance_past_semi().ok_or(ErrorInfo {
error: ParseError::ExpectedToken(Token::Semi),
loc: tokens.current_source_location(),
})?;
}
_ => unimplemented!(),
}
Ok(this.push_error(err.error, err.loc).into())
})
}
fn parse_bracketed<F, I>(&mut self, tokens: &mut TokenIterator, parse: F) -> ParseResult<I>
where
F: FnOnce(&mut Self, &mut TokenIterator) -> ParseResult<I>,
Index: Into<I>,
{
self.parse_inner2(
tokens,
Token::OpenSquareBracket,
Token::CloseSquareBracket,
parse,
)
}
fn parse_braced<F, I>(&mut self, tokens: &mut TokenIterator, parse: F) -> ParseResult<I>
where
F: FnOnce(&mut Self, &mut TokenIterator) -> ParseResult<I>,
Index: Into<I>,
{
self.parse_inner2(tokens, Token::OpenBrace, Token::CloseBrace, parse)
}
fn parse_parenthesised<F, I>(
&mut self,
tokens: &mut TokenIterator,
parse: F,
) -> ParseResult<I>
where
F: FnOnce(&mut Self, &mut TokenIterator) -> ParseResult<I>,
Index: Into<I>,
{
self.parse_inner2(tokens, Token::OpenParens, Token::CloseParens, parse)
}
fn parse_struct_fields(
&mut self,
tokens: &mut TokenIterator,
) -> ParseResult<Vec<(intern::Index, Index)>> {
let mut fields = Vec::new();
loop {
fields.push(self.parse_struct_field(tokens)?);
if !tokens.is_next_token(Token::Comma) {
break;
}
if tokens.is_next_token2(Token::CloseBrace) {
break;
}
// skip comma
_ = tokens.next();
}
Ok(fields)
}
/// STRUCT_FIELD <-
/// IDENTIFIER: TYPE
fn parse_struct_field(
&mut self,
tokens: &mut TokenIterator,
) -> ParseResult<(intern::Index, Index)> {
let name = self.parse_ident(tokens)?;
let Some(_) = tokens.eat_token(Token::Colon) else {
return Err(ErrorInfo {
error: ParseError::ExpectedToken(Token::Colon),
loc: tokens.current_source_location(),
});
};
let ty = self.parse_type(tokens)?;
return Ok((name, ty));
}
/// CONSTANT_DECL <-
/// FUNCTION_DECL
/// GLOBAL_DECL
/// STRUCT_DECL
fn parse_constant_decls(
&mut self,
tokens: &mut TokenIterator,
) -> ParseResult<Option<Index>> {
let next = tokens.peek_token().ok_or(ErrorInfo {
error: ParseError::UnexpectedEndOfTokens,
loc: tokens.current_source_location(),
})?;
match next.token() {
Token::Fn => Ok(Some(self.parse_fn_decl(tokens))),
Token::Const => self.parse_const_decl(tokens).map(|i| Some(i)),
Token::Type => self.parse_type_decl(tokens).map(|i| Some(i)),
_ => Ok(None),
}
}
/// FILE <-
/// (FUNCTION_DECL | GLOBAL_DECL)*
fn parse_file(&mut self, tokens: &mut TokenIterator) -> Index {
let start = tokens.current_source_location();
let mut decls = Vec::new();
let file = self.ast.reserve_node(Kind::File);
self.push_scope(file, intern::Index::invalid());
while let Some(next) = tokens.peek_token() {
let loc = next.source_location();
let decl = match self.parse_constant_decls(tokens).and_then(|i| match i {
Some(i) => Ok(i),
None => {
let error = ParseError::UnexpectedTokenAtFileScope;
let node = self.push_error(error, loc);
self.find_next_fn_or_const(tokens);
Ok(node)
}
}) {
Ok(i) => i,
Err(err) => self.push_error(err.error, err.loc),
};
decls.push(decl);
}
self.pop_scope();
self.ast.set_file(file, decls, start);
file
}
/// FILE <-
/// (FUNCTION_DECL | GLOBAL_DECL)*
pub fn parse(&mut self, mut tokens: TokenIterator) {
let file = self.parse_file(&mut tokens);
self.ast.set_root([file]);
eprintln!("resolving decls:");
self.resolve_decl_refs();
self.create_comptime_folding_graph(intern::AMD64_POINTER_BITS);
eprintln!("interning types:");
self.intern_types();
self.fold_more_patterns();
}
fn push_scope(&mut self, ast: Index, name: intern::Index) {
let parent = self.scopes.last().cloned();
self.scopes.push(ast);
if let Some(parent) = parent {
self.syms.insert_symbol(
ast,
intern::Index::invalid(),
SymbolKind::ParentScope,
parent,
);
}
self.syms.insert_scope(name, ast);
}
fn pop_scope(&mut self) {
self.scopes.pop();
}
fn is_statement(&self, tokens: &mut TokenIterator) -> bool {
let mut tokens = tokens.clone();
let mut braces = 0;
let mut parens = 0;
let mut brackets = 0;
while let Some(itm) = tokens.next() {
match itm.token() {
Token::OpenBrace => {
braces += 1;
}
Token::CloseBrace => {
braces -= 1;
}
Token::OpenParens => {
parens += 1;
}
Token::CloseParens => {
parens -= 1;
}
Token::OpenSquareBracket => {
brackets += 1;
}
Token::CloseSquareBracket => {
brackets -= 1;
}
Token::Semi => {
if braces == 0 && parens == 0 && brackets == 0 {
return true;
}
}
_ => {}
}
if braces < 0 || parens < 0 || brackets < 0 {
break;
}
}
false
}
fn find_next_fn_or_const(&mut self, tokens: &mut TokenIterator) -> Option<()> {
tokens
.advance_until_before_one_of(&[Token::Const, Token::Fn, Token::Type])
.map(|_| ())
}
}
}
impl Ast {
fn visit_all_functions_mut<V>(&mut self, visitor: &mut impl AstVisitorTrait) {
for i in
(0..self.tags.functions.len()).map(|i| Index::new(Kind::Function, i as u32).unwrap())
{
visitor.visit_function_decl(self, i);
}
}
fn visit_function_mut<F: FnMut(&mut Self)>(&mut self) {}
}
pub trait AstVisitorTrait {
type Error;
type Value;
const UNIMPL: Self::Error;
fn visit_function_decl(
&mut self,
ast: &mut Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_parameter(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_parameter_list(
&mut self,
ast: &mut Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_block(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_block_trailing_expr(
&mut self,
ast: &mut Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_block_maybe_trailing(
&mut self,
ast: &mut Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
use visitor::AstExt;
match ast.get_node_tag_and_data(idx).0 {
Tag::BlockTrailingExpr => self.visit_block(ast, idx),
Tag::Block => self.visit_block_trailing_expr(ast, idx),
_ => unreachable!(),
}
}
fn visit_function_proto(
&mut self,
ast: &mut Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_call_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_add_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_sub_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_mul_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_div_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_rem_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_eq_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_neq_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_lt_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_gt_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_le_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_ge_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_shl_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_shr_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_bitor_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_bitxor_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_bitand_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_or_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_and_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_not_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_negate_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_deref_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_address_of_expr(
&mut self,
ast: &mut Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_explicit_cast_expr(
&mut self,
ast: &mut Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_assign(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_subscript_expr(
&mut self,
ast: &mut Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_if_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_if_else_expr(
&mut self,
ast: &mut Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_argument(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_any_argument(
&mut self,
ast: &mut Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
use visitor::AstExt;
match ast.get_node_tag_and_data(idx).0 {
Tag::Argument => self.visit_argument(ast, idx),
Tag::NamedArgument => self.visit_named_argument(ast, idx),
_ => unreachable!(),
}
}
fn visit_named_argument(
&mut self,
ast: &mut Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_argument_list(
&mut self,
ast: &mut Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_constant(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_return(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_return_expr(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_global_decl(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_var_decl(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_mut_var_decl(
&mut self,
ast: &mut Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_var_assign_decl(
&mut self,
ast: &mut Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_mut_var_assign_decl(
&mut self,
ast: &mut Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_decl_ref(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
Err(Self::UNIMPL)
}
fn visit_value_to_place_conversion(
&mut self,
ast: &mut Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let idx = ast
.get_node_data_for_tag(idx, Tag::ValueToPlaceConversion)
.unwrap()
.as_index();
self.visit_any(ast, idx)
}
fn visit_place_to_value_conversion(
&mut self,
ast: &mut Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let idx = ast
.get_node_data_for_tag(idx, Tag::PlaceToValueConversion)
.unwrap()
.as_index();
self.visit_any(ast, idx)
}
fn visit_any(&mut self, ast: &mut Ast, idx: Index) -> Result<Self::Value, Self::Error> {
use visitor::AstExt;
match ast.get_node_tag_and_data(idx).0 {
Tag::FunctionProto => self.visit_function_proto(ast, idx),
Tag::FunctionDecl => self.visit_function_decl(ast, idx),
Tag::ParameterList => self.visit_parameter_list(ast, idx),
Tag::Parameter => self.visit_parameter(ast, idx),
Tag::Block => self.visit_block(ast, idx),
Tag::BlockTrailingExpr => self.visit_block_trailing_expr(ast, idx),
Tag::Constant => self.visit_constant(ast, idx),
Tag::ExprStmt => todo!(),
Tag::ReturnStmt => self.visit_return(ast, idx),
Tag::ReturnExprStmt => self.visit_return_expr(ast, idx),
Tag::VarDecl => self.visit_var_decl(ast, idx),
Tag::MutVarDecl => self.visit_mut_var_decl(ast, idx),
Tag::VarDeclAssignment => self.visit_var_assign_decl(ast, idx),
Tag::MutVarDeclAssignment => self.visit_mut_var_assign_decl(ast, idx),
Tag::GlobalDecl => self.visit_global_decl(ast, idx),
Tag::StructDecl => todo!(),
Tag::FieldDecl => todo!(),
Tag::DeclRef => self.visit_decl_ref(ast, idx),
Tag::DeclRefUnresolved => todo!(),
Tag::InternedType => todo!(),
Tag::TypeDeclRef => todo!(),
Tag::TypeDeclRefUnresolved => todo!(),
Tag::PointerType => todo!(),
Tag::ArrayType => todo!(),
Tag::CallExpr => self.visit_call_expr(ast, idx),
Tag::FieldAccess => todo!(),
Tag::ArgumentList => self.visit_argument_list(ast, idx),
Tag::Argument => self.visit_argument(ast, idx),
Tag::NamedArgument => self.visit_named_argument(ast, idx),
Tag::ExplicitCast => self.visit_explicit_cast_expr(ast, idx),
Tag::Deref => self.visit_deref_expr(ast, idx),
Tag::AddressOf => self.visit_add_expr(ast, idx),
Tag::Not => self.visit_not_expr(ast, idx),
Tag::Negate => self.visit_negate_expr(ast, idx),
Tag::Or => self.visit_or_expr(ast, idx),
Tag::And => self.visit_and_expr(ast, idx),
Tag::BitOr => self.visit_bitor_expr(ast, idx),
Tag::BitXOr => self.visit_bitxor_expr(ast, idx),
Tag::BitAnd => self.visit_bitand_expr(ast, idx),
Tag::Eq => self.visit_eq_expr(ast, idx),
Tag::NEq => self.visit_neq_expr(ast, idx),
Tag::Lt => self.visit_lt_expr(ast, idx),
Tag::Gt => self.visit_gt_expr(ast, idx),
Tag::Le => self.visit_le_expr(ast, idx),
Tag::Ge => self.visit_ge_expr(ast, idx),
Tag::Shl => self.visit_shl_expr(ast, idx),
Tag::Shr => self.visit_shr_expr(ast, idx),
Tag::Add => self.visit_add_expr(ast, idx),
Tag::Sub => self.visit_sub_expr(ast, idx),
Tag::Mul => self.visit_mul_expr(ast, idx),
Tag::Div => self.visit_div_expr(ast, idx),
Tag::Rem => self.visit_rem_expr(ast, idx),
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::PlaceToValueConversion => self.visit_place_to_value_conversion(ast, idx),
Tag::ValueToPlaceConversion => self.visit_value_to_place_conversion(ast, idx),
Tag::Error => todo!(),
Tag::Undefined => todo!(),
Tag::Root => todo!(),
Tag::File => todo!(),
}
}
}
impl Ast {
fn get_node_data_for_tag(&self, idx: Index, tag: Tag) -> Option<Data> {
use visitor::AstExt;
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:?}"))
}
}
pub mod ir {
use std::collections::HashMap;
use crate::{
ast2::{
intern::{self, AMD64_POINTER_TYPE_INFO},
Index,
},
triples::{self, Inst, IR},
};
#[derive(Debug, thiserror::Error)]
enum Error {
#[error("This visitor does not implement visiting this tag.")]
Unimplemented,
}
use super::{intern::InternPool, visitor::AstExt, AstVisitorTrait, TypeCache};
struct IrBuilder {
functions: Vec<Function>,
}
struct Function {
ir: IR,
}
struct IrFunctionBuilder<'a> {
ir: &'a mut IR,
type_cache: &'a mut TypeCache,
ref_lookup: &'a mut HashMap<Index, u32>,
ip: &'a InternPool,
function: Function,
}
impl<'a> AstVisitorTrait for IrFunctionBuilder<'a> {
type Error = Error;
type Value = Option<u32>;
const UNIMPL: Self::Error = Error::Unimplemented;
fn visit_block(
&mut self,
ast: &mut super::Ast,
idx: super::Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::Block);
let (a, b) = data.as_extra_range();
ast.extra[a..b]
.to_vec()
.into_iter()
.map(|i| Index::from_u32(i).unwrap())
.map(|i| self.visit_any(ast, i))
.try_fold((), |_, e| e.map(crate::unit))?;
Ok(None)
}
fn visit_place_to_value_conversion(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::PlaceToValueConversion);
let expr = data.as_index();
// idx's (this) type is the pointee type
let ty = ast.get_type_of_node(&self.ip, &mut self.type_cache, idx);
let expr = self.visit_any(ast, expr)?.unwrap();
let ir = self.ir.push(Inst::Load(ty), Some(triples::Data::lhs(expr)));
Ok(Some(ir))
}
fn visit_value_to_place_conversion(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::ValueToPlaceConversion);
let expr = data.as_index();
// expr's type is the pointee type
let ty = ast.get_type_of_node(&self.ip, &mut self.type_cache, expr);
let expr = self.visit_any(ast, expr)?.unwrap();
let info = self.ip.size_of_type(ty, AMD64_POINTER_TYPE_INFO);
let alloca = self.ir.push(
Inst::Alloca,
Some(triples::Data::new(info.size(), info.align())),
);
_ = self
.ir
.push(Inst::Store(ty), Some(triples::Data::new(expr, alloca)));
Ok(Some(alloca))
}
fn visit_decl_ref(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::DeclRef);
let decl = data.as_index();
let alloca = self.ref_lookup.get(&decl).cloned().expect("declref");
Ok(Some(alloca))
}
fn visit_var_decl(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_var_decl_common(ast, idx)
}
fn visit_mut_var_decl(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_var_decl_common(ast, idx)
}
fn visit_var_assign_decl(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_var_decl_common(ast, idx)
}
fn visit_mut_var_assign_decl(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_var_decl_common(ast, idx)
}
fn visit_return(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
_ = (ast, idx);
let ir = self.ir.push(Inst::Return, None);
Ok(Some(ir))
}
fn visit_return_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::ReturnExprStmt);
let expr = data.as_index();
let ty = ast.get_type_of_node(&self.ip, &mut self.type_cache, expr);
let expr = self.visit_any(ast, expr)?.unwrap();
let ir = self
.ir
.push(Inst::ReturnValue(ty), Some(triples::Data::lhs(expr)));
Ok(Some(ir))
}
fn visit_constant(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::Constant);
let (ty, value) = data.as_index_intern();
let ty = ast.get_type_of_node(&self.ip, &mut self.type_cache, ty);
let ir = self.ir.push(Inst::Constant, Some((ty, value).into()));
Ok(Some(ir))
}
fn visit_subscript_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::SubscriptExpr);
let (lhs, index) = data.as_two_indices();
// pointer type
let ty = ast.get_type_of_node(&self.ip, &mut self.type_cache, idx);
let lhs = self.visit_any(ast, lhs)?.unwrap();
let index = self.visit_any(ast, index)?.unwrap();
// TODO: make getelementptr take a variable instead of a constant index.
let ir = self.ir.push(
Inst::GetElementPtr(ty),
Some(triples::Data::new(lhs, index)),
);
Ok(Some(ir))
}
fn visit_call_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::CallExpr);
let (func, arguments) = data.as_two_indices();
let ty = ast.get_type_of_node(&self.ip, &mut self.type_cache, idx);
let len = self.visit_parameter_list(ast, arguments)?.unwrap();
_ = self.ir.push(Inst::InlineType(ty), None);
let end = self.ir.nodes.len() as u32;
let func = self.visit_any(ast, func)?.unwrap();
let result = self
.ir
.push(Inst::Call(func), Some(triples::Data::new(end - len, end)));
Ok(Some(result))
}
fn visit_argument(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::Argument);
let expr = data.as_index();
let arg = self.visit_any(ast, expr)?.unwrap();
// let ty = ast.get_type_of_node(&self.ip, &mut self.type_cache, expr);
// self.ir.push(Inst::Argument(ty), Some(triples::Data::lhs(arg)));
Ok(Some(arg))
}
// fn visit_named_argument(
// &mut self,
// ast: &mut super::Ast,
// idx: Index,
// ) -> Result<Self::Value, Self::Error> {
// todo!()
// }
fn visit_argument_list(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::ArgumentList);
let (a, b) = data.as_extra_range();
let args = ast.extra[a..b]
.to_vec()
.into_iter()
.map(|i| Index::from_u32(i).unwrap())
.map(|i| {
let ty = ast.get_type_of_node(&self.ip, &mut self.type_cache, i);
let arg = self.visit_any_argument(ast, i)?.unwrap();
Ok((ty, arg))
})
.collect::<Result<Vec<_>, _>>()?;
let count = args.len();
for (ty, arg) in args {
_ = self
.ir
.push(Inst::Argument(ty), Some(triples::Data::lhs(arg)));
}
// .try_fold((), |_, e| e.map(crate::unit))?;
Ok(Some(count as u32))
}
fn visit_block_trailing_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::BlockTrailingExpr);
let (a, b) = data.as_extra_range();
let expr = ast.extra[a..b]
.to_vec()
.into_iter()
.map(|i| Index::from_u32(i).unwrap())
.map(|i| self.visit_any(ast, i))
// the last node is the trailing
.try_fold(None, |_, e| e)?;
Ok(expr)
}
fn visit_function_proto(
&mut self,
ast: &mut super::Ast,
idx: super::Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::FunctionProto);
let (_, extra) = data.as_intern_and_extra_offset();
let (_return_type, parameter_list) = (
Index::from_u32(ast.extra[extra]).unwrap(),
Index::from_u32(ast.extra[extra + 1]).unwrap(),
);
self.visit_parameter_list(ast, parameter_list)?;
// push parameters to ir
Ok(None)
}
fn visit_parameter_list(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::ParameterList);
let (a, b) = data.as_extra_range();
ast.extra[a..b]
.to_vec()
.into_iter()
.map(|i| Index::from_u32(i).unwrap())
.map(|i| self.visit_parameter(ast, i))
.try_fold((), |_, e| e.map(crate::unit))?;
Ok(None)
}
fn visit_parameter(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::Parameter);
let (ty, _name) = data.as_index_intern();
let ty = ast.get_type_of_node(&self.ip, &mut self.type_cache, ty);
let info = self.ip.size_of_type(ty, AMD64_POINTER_TYPE_INFO);
let ir = self.ir.push(
Inst::Parameter(ty),
Some(triples::Data::new(info.size(), info.align())),
);
self.ref_lookup.insert(idx, ir);
Ok(None)
}
fn visit_address_of_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::AddressOf);
let expr = data.as_index();
let ty = ast.get_type_of_node(&self.ip, &mut self.type_cache, expr);
let expr = self.visit_any(ast, expr)?.unwrap();
let r = self
.ir
.push(Inst::GetElementPtr(ty), Some(triples::Data::new(expr, 0)));
Ok(Some(r))
}
fn visit_assign(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::Assign);
let (dst, src) = data.as_two_indices();
let ty = ast.get_type_of_node(&self.ip, &mut self.type_cache, idx);
let dst = self.visit_any(ast, dst)?.unwrap();
let src = self.visit_any(ast, src)?.unwrap();
let ir = self
.ir
.push(Inst::Store(ty.into()), Some(triples::Data::new(src, dst)));
Ok(Some(ir))
}
fn visit_explicit_cast_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::ExplicitCast);
let (lhs, ty) = data.as_two_indices();
let lty = ast.get_type_of_node(&self.ip, &mut self.type_cache, lhs);
let rty = ast.get_type_of_node(&self.ip, &mut self.type_cache, ty);
let lhs = self.visit_any(ast, lhs)?.unwrap();
// TODO: if bitwidth is the same, then this is a noop?
let cast = self
.ir
.push(Inst::ExplicitCast(lty, rty), Some(triples::Data::lhs(lhs)));
Ok(Some(cast))
}
fn visit_if_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::IfExpr);
let (cond, body) = data.as_two_indices();
let cond = self.visit_any(ast, cond)?.unwrap();
let br = self.ir.push(Inst::Branch(cond), None);
let lbody = self
.ir
.push(Inst::Label, Some(intern::Index::EMPTY_STRING.into()));
_ = self.visit_any(ast, body);
let jmp = self.ir.push(Inst::Jump, None);
let skip = self
.ir
.push(Inst::Label, Some(intern::Index::EMPTY_STRING.into()));
self.ir.data[br as usize] = Some(triples::Data::new(lbody, skip));
self.ir.data[jmp as usize] = Some(triples::Data::lhs(skip));
Ok(Some(br))
}
fn visit_if_else_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::IfElseExpr);
let (cond, extra) = data.as_index_and_extra_offset();
let ty = ast.get_type_of_node(&self.ip, &mut self.type_cache, idx);
let [a, b] = ast.extra[extra..][..2] else {
unreachable!()
};
let cond = self.visit_any(ast, cond)?.unwrap();
let br = self.ir.push(Inst::Branch(cond), None);
let la = self
.ir
.push(Inst::Label, Some(intern::Index::EMPTY_STRING.into()));
let a = self.visit_any(ast, Index::from_u32(a).unwrap())?.unwrap();
let jmpa = self.ir.push(Inst::Jump, None);
let lb = self
.ir
.push(Inst::Label, Some(intern::Index::EMPTY_STRING.into()));
let b = self.visit_any(ast, Index::from_u32(b).unwrap())?.unwrap();
let jmpb = self.ir.push(Inst::Jump, None);
let lphi = self
.ir
.push(Inst::Label, Some(intern::Index::EMPTY_STRING.into()));
let phi = if ty == intern::Index::VOID {
br
} else {
self.ir.push(Inst::Phi2(ty), Some(triples::Data::new(a, b)))
};
self.ir.data[br as usize] = Some(triples::Data::new(la, lb));
self.ir.data[jmpa as usize] = Some(triples::Data::lhs(lphi));
self.ir.data[jmpb as usize] = Some(triples::Data::lhs(lphi));
Ok(Some(phi))
}
fn visit_deref_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::Not);
let expr = data.as_index();
let ty = ast.get_type_of_node(&self.ip, &mut self.type_cache, idx);
let expr = self.visit_any(ast, expr)?.unwrap();
let not = self.ir.push(Inst::Load(ty), Some(triples::Data::lhs(expr)));
Ok(Some(not))
}
fn visit_not_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::Not);
let expr = data.as_index();
let ty = ast.get_type_of_node(&self.ip, &mut self.type_cache, expr);
let expr = self.visit_any(ast, expr)?.unwrap();
let not = self.ir.push(Inst::Not(ty), Some(triples::Data::lhs(expr)));
Ok(Some(not))
}
fn visit_negate_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::Negate);
let expr = data.as_index();
let ty = ast.get_type_of_node(&self.ip, &mut self.type_cache, expr);
let expr = self.visit_any(ast, expr)?.unwrap();
let not = self
.ir
.push(Inst::Negate(ty), Some(triples::Data::lhs(expr)));
Ok(Some(not))
}
fn visit_or_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::Or);
let (a, b) = data.as_two_indices();
let ty = ast.get_type_of_node(&self.ip, &mut self.type_cache, idx);
// TODO: make this an error instead of a panic
let a = self.visit_any(ast, a)?.unwrap();
let not_a = self.ir.push(Inst::Not(ty), Some(triples::Data::lhs(a)));
// branch taken if a
let br = self.ir.push(Inst::Branch(not_a), None);
let lb = self
.ir
.push(Inst::Label, Some(intern::Index::EMPTY_STRING.into()));
let b = self.visit_any(ast, b)?.unwrap();
let jmp = self.ir.push(Inst::Jump, None);
let la = self
.ir
.push(Inst::Label, Some(intern::Index::EMPTY_STRING.into()));
self.ir.data[br as usize] = Some(triples::Data::new(lb, la));
self.ir.data[jmp as usize] = Some(triples::Data::lhs(la));
let or = self.ir.push(
Inst::Phi2(intern::Index::BOOL),
Some(triples::Data::new(a, b)),
);
Ok(Some(or))
}
fn visit_and_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::Or);
let (a, b) = data.as_two_indices();
// TODO: make this an error instead of a panic
let a = self.visit_any(ast, a)?.unwrap();
// branch taken if a
let br = self.ir.push(Inst::Branch(a), None);
let lb = self
.ir
.push(Inst::Label, Some(intern::Index::EMPTY_STRING.into()));
let b = self.visit_any(ast, b)?.unwrap();
let skip = self
.ir
.push(Inst::Label, Some(intern::Index::EMPTY_STRING.into()));
self.ir.data[br as usize] = Some(triples::Data::new(lb, skip));
let and = self.ir.push(
Inst::Phi2(intern::Index::BOOL),
Some(triples::Data::new(a, b)),
);
Ok(Some(and))
}
fn visit_add_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_binop_expr(ast, idx)
}
fn visit_sub_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_binop_expr(ast, idx)
}
fn visit_div_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_binop_expr(ast, idx)
}
fn visit_rem_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_binop_expr(ast, idx)
}
fn visit_mul_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_binop_expr(ast, idx)
}
fn visit_bitand_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_binop_expr(ast, idx)
}
fn visit_bitor_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_binop_expr(ast, idx)
}
fn visit_bitxor_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_binop_expr(ast, idx)
}
fn visit_eq_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_binop_expr(ast, idx)
}
fn visit_neq_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_binop_expr(ast, idx)
}
fn visit_lt_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_binop_expr(ast, idx)
}
fn visit_gt_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_binop_expr(ast, idx)
}
fn visit_le_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_binop_expr(ast, idx)
}
fn visit_ge_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_binop_expr(ast, idx)
}
fn visit_shl_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_binop_expr(ast, idx)
}
fn visit_shr_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Self::Value, Self::Error> {
self.visit_binop_expr(ast, idx)
}
}
impl IrFunctionBuilder<'_> {
fn visit_var_decl_common(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Option<u32>, Error> {
let (tag, data) = ast.get_node_tag_and_data(idx);
let (a, b) = data.as_extra_range();
let range = &ast.extra[a..b];
let _name = range.get(0).unwrap();
let (expr, typed_index) = match tag {
super::Tag::VarDecl | super::Tag::MutVarDecl => {
let typed_index = range
.get(1)
.cloned()
.map(Index::from_u32)
.flatten()
.unwrap();
(None, typed_index)
}
super::Tag::VarDeclAssignment | super::Tag::MutVarDeclAssignment => {
let expr = range
.get(1)
.cloned()
.map(Index::from_u32)
.flatten()
.unwrap();
let typed_index = range
.get(2)
.cloned()
.map(Index::from_u32)
.flatten()
.unwrap_or(expr);
(Some(expr), typed_index)
}
_ => unreachable!(),
};
let ty = ast.get_type_of_node(&self.ip, &mut self.type_cache, typed_index);
let info = self.ip.size_of_type(ty, AMD64_POINTER_TYPE_INFO);
let alloca = self.ir.push(
Inst::Alloca,
Some(triples::Data::new(info.size(), info.align())),
);
if let Some(idx) = expr {
let expr = self.visit_any(ast, idx)?.unwrap();
_ = self
.ir
.push(Inst::Store(ty), Some(triples::Data::new(expr, alloca)));
}
self.ref_lookup.insert(idx, alloca);
Ok(Some(alloca))
}
/// handles all binary operations that don't short circuit and visit
/// both lhs and rhs.
fn visit_binop_expr(
&mut self,
ast: &mut super::Ast,
idx: Index,
) -> Result<Option<u32>, Error> {
use triples::Data;
let (tag, data) = ast.get_node_tag_and_data(idx);
let (lhs, rhs) = data.as_two_indices();
let ty = ast.get_type_of_node(&self.ip, &mut self.type_cache, idx);
// TODO: make this an error instead of a panic
let lhs = self.visit_any(ast, lhs)?.unwrap();
let rhs = self.visit_any(ast, rhs)?.unwrap();
let (inst, data) = match tag {
super::Tag::BitOr => (Inst::BitOr(ty), Data::new(lhs, rhs)),
super::Tag::BitXOr => (Inst::BitOr(ty), Data::new(lhs, rhs)),
super::Tag::BitAnd => (Inst::BitOr(ty), Data::new(lhs, rhs)),
super::Tag::Eq => (Inst::Eq(ty), Data::new(lhs, rhs)),
super::Tag::NEq => (Inst::Neq(ty), Data::new(lhs, rhs)),
super::Tag::Lt => (Inst::Lt(ty), Data::new(lhs, rhs)),
super::Tag::Gt => (Inst::Gt(ty), Data::new(lhs, rhs)),
super::Tag::Le => (Inst::Le(ty), Data::new(lhs, rhs)),
super::Tag::Ge => (Inst::Ge(ty), Data::new(lhs, rhs)),
super::Tag::Shl => (Inst::ShiftLeft(ty), Data::new(lhs, rhs)),
super::Tag::Shr => (Inst::ShiftRight(ty), Data::new(lhs, rhs)),
super::Tag::Add => (Inst::Add(ty), Data::new(lhs, rhs)),
super::Tag::Sub => (Inst::Sub(ty), Data::new(lhs, rhs)),
super::Tag::Div => (Inst::Div(ty), Data::new(lhs, rhs)),
super::Tag::Rem => (Inst::Rem(ty), Data::new(lhs, rhs)),
super::Tag::Mul => (Inst::Mul(ty), Data::new(lhs, rhs)),
_ => panic!("not a binop"),
};
Ok(Some(self.ir.push(inst, Some(data))))
}
}
impl AstVisitorTrait for IrBuilder {
type Value = ();
type Error = Error;
const UNIMPL: Self::Error = Error::Unimplemented;
fn visit_function_decl(
&mut self,
ast: &mut super::Ast,
idx: super::Index,
) -> Result<(), Self::Error> {
let data = ast.expect_node_data_for_tag(idx, super::Tag::FunctionDecl);
let (proto, block) = data.as_two_indices();
// visit proto
// visit block
todo!()
}
}
}
pub mod irgen {
use super::*;
use crate::{symbol_table::syms2::Symbols, triples::*};
use std::collections::HashMap;
struct IRGen {
ast: Ast,
syms: Symbols,
ir: IR,
ip: intern::InternPool,
}
impl IRGen {
fn build(&mut self) {
let mut mapping = HashMap::<Index, u32>::new();
self.ast.visitor().visit(
|ast, scopes, i, tag, data| {
_ = (&mapping, ast, scopes, i, tag, data);
// pre
match tag {
Tag::Root => todo!(),
Tag::File => todo!(),
Tag::FunctionProto => todo!(),
Tag::FunctionDecl => todo!(),
Tag::ParameterList => todo!(),
Tag::Parameter => todo!(),
Tag::Block => todo!(),
Tag::BlockTrailingExpr => todo!(),
Tag::Constant => todo!(),
Tag::ExprStmt => todo!(),
Tag::ReturnStmt => todo!(),
Tag::ReturnExprStmt => todo!(),
Tag::VarDecl => todo!(),
Tag::MutVarDecl => todo!(),
Tag::VarDeclAssignment => todo!(),
Tag::MutVarDeclAssignment => todo!(),
Tag::GlobalDecl => todo!(),
Tag::StructDecl => todo!(),
Tag::FieldDecl => todo!(),
Tag::DeclRef => todo!(),
Tag::DeclRefUnresolved => todo!(),
Tag::InternedType => todo!(),
Tag::TypeDeclRef => todo!(),
Tag::TypeDeclRefUnresolved => todo!(),
Tag::PointerType => todo!(),
Tag::ArrayType => todo!(),
Tag::CallExpr => todo!(),
Tag::FieldAccess => todo!(),
Tag::ArgumentList => todo!(),
Tag::Argument => todo!(),
Tag::NamedArgument => todo!(),
Tag::ExplicitCast => todo!(),
Tag::Deref => todo!(),
Tag::AddressOf => todo!(),
Tag::Not => todo!(),
Tag::Negate => todo!(),
Tag::Or => todo!(),
Tag::And => todo!(),
Tag::BitOr => todo!(),
Tag::BitXOr => todo!(),
Tag::BitAnd => todo!(),
Tag::Eq => todo!(),
Tag::NEq => todo!(),
Tag::Lt => todo!(),
Tag::Gt => todo!(),
Tag::Le => todo!(),
Tag::Ge => todo!(),
Tag::Shl => todo!(),
Tag::Shr => todo!(),
Tag::Add => todo!(),
Tag::Sub => todo!(),
Tag::Mul => todo!(),
Tag::Div => todo!(),
Tag::Rem => todo!(),
Tag::Assign => todo!(),
Tag::SubscriptExpr => todo!(),
Tag::IfExpr => todo!(),
Tag::IfElseExpr => todo!(),
Tag::Error => todo!(),
Tag::Undefined => todo!(),
Tag::PlaceToValueConversion => todo!(),
Tag::ValueToPlaceConversion => todo!(),
}
},
|ast, scopes, i, tag, data| {
_ = (&mapping, ast, scopes, i, tag, data);
// post
match tag {
Tag::Root => todo!(),
Tag::File => todo!(),
Tag::FunctionProto => todo!(),
Tag::FunctionDecl => {
todo!()
}
Tag::ParameterList => todo!(),
Tag::Parameter => todo!(),
Tag::Block => todo!(),
Tag::BlockTrailingExpr => todo!(),
Tag::Constant => todo!(),
Tag::ExprStmt => todo!(),
Tag::ReturnStmt => todo!(),
Tag::ReturnExprStmt => todo!(),
Tag::VarDecl => todo!(),
Tag::MutVarDecl => todo!(),
Tag::VarDeclAssignment => todo!(),
Tag::MutVarDeclAssignment => todo!(),
Tag::GlobalDecl => todo!(),
Tag::StructDecl => todo!(),
Tag::FieldDecl => todo!(),
Tag::DeclRef => todo!(),
Tag::DeclRefUnresolved => todo!(),
Tag::InternedType => todo!(),
Tag::TypeDeclRef => todo!(),
Tag::TypeDeclRefUnresolved => todo!(),
Tag::PointerType => todo!(),
Tag::ArrayType => todo!(),
Tag::CallExpr => todo!(),
Tag::FieldAccess => todo!(),
Tag::ArgumentList => todo!(),
Tag::Argument => todo!(),
Tag::NamedArgument => todo!(),
Tag::ExplicitCast => todo!(),
Tag::Deref => todo!(),
Tag::AddressOf => todo!(),
Tag::Not => todo!(),
Tag::Negate => todo!(),
Tag::Or => todo!(),
Tag::And => todo!(),
Tag::BitOr => todo!(),
Tag::BitXOr => todo!(),
Tag::BitAnd => todo!(),
Tag::Eq => todo!(),
Tag::NEq => todo!(),
Tag::Lt => todo!(),
Tag::Gt => todo!(),
Tag::Le => todo!(),
Tag::Ge => todo!(),
Tag::Shl => todo!(),
Tag::Shr => todo!(),
Tag::Add => todo!(),
Tag::Sub => todo!(),
Tag::Mul => todo!(),
Tag::Div => todo!(),
Tag::Rem => todo!(),
Tag::Assign => todo!(),
Tag::SubscriptExpr => todo!(),
Tag::IfExpr => todo!(),
Tag::IfElseExpr => todo!(),
Tag::Error => todo!(),
Tag::Undefined => todo!(),
Tag::PlaceToValueConversion => todo!(),
Tag::ValueToPlaceConversion => todo!(),
}
},
);
}
}
}