#![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 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 for Kind { type Error = (); fn try_from(value: u8) -> Result { 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); 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 { 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 { 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 { functions: Vec, global_decls: Vec, errors: Vec, files: Vec, other: Vec, } impl core::ops::Index for AstTables { type Output = T; fn index(&self, index: index::Index) -> &Self::Output { &self.table_ref(index.kind())[index.index_usize()] } } impl core::ops::IndexMut for AstTables { fn index_mut(&mut self, index: index::Index) -> &mut Self::Output { &mut self.table_mut(index.kind())[index.index_usize()] } } impl AstTables { 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 { 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 { 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 { 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, datas: AstTables, source_locs: AstTables, extra: Vec, } 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>(&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>(&mut self, decls: I, loc: SourceLocation) -> Index { let i = self.reserve_node(Kind::File); self.set_file(i, decls, loc); i } fn set_root>(&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 + '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, 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, assignment: Option, 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>( &mut self, i: Index, statements: I, trailing: Option, 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>( &mut self, statements: I, trailing: Option, loc: SourceLocation, ) -> Index { let i = self.reserve_node_other(); self.set_block(i, statements, trailing, loc); i } fn push_parameter_list>( &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>( &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>( &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>( &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>(&mut self, indices: I) -> (u32, u32) { self.extend_extra(indices.into_iter().map(|i| i.as_u32())) } fn extend_extra>(&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); 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; // type ComptimeCache = BTreeMap; #[derive(Debug, Default)] struct ComptimeCache { inner: BTreeMap, // 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, } impl ComptimeCache { fn get(&self, key: &Index) -> Option { 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 { 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 { 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; fn get_node_tag_and_data(&self, node: Index) -> (Tag, Data); } impl AstExt for &Ast { fn get_node_children(&self, node: Index) -> Vec { 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 { 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 { pub(super) ast: AstT, pub(super) scopes: Vec, pub(super) nodes: Vec, pub(super) rev: bool, } impl AstVisitor 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(&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(&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(&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, pub errors: Vec, } type ParseResult = core::result::Result; 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::>(); 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::::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::>(); 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 { 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 { 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 { 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 { 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, 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::>(); 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::().unwrap(), }) } else { self.intern.get_or_insert(intern::Key::F64 { bits: lexeme.0.parse::().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 { 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 { 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 { 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 { // 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 { 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 { // 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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> { 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 { 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 { 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 { 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 { 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 { // 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 { // 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 { 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 { 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 { // 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 { // 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 { _ = 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 { todo!() } /// TUPLE_DECL <- /// type IDENTIFIER = (TYPE,* ) fn parse_tuple_decl(&mut self, _tokens: &mut TokenIterator) -> ParseResult { todo!() } /// UNION_DECL <- /// type IDENTIFIER = union { IDENTIFIER: TYPE,* } fn parse_union_decl(&mut self, _tokens: &mut TokenIterator) -> ParseResult { todo!() } /// ENUM_DECL <- /// type IDENTIFIER = packed? enum { IDENTIFIER (= EXPRESSION),* } fn parse_enum_decl(&mut self, _tokens: &mut TokenIterator) -> ParseResult { 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 { // 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( &mut self, tokens: &mut TokenIterator, parse: F, ) -> ParseResult where F: FnOnce(&mut Self, &mut TokenIterator) -> ParseResult, Index: Into, { 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( &mut self, tokens: &mut TokenIterator, open: Token, close: Token, parse: F, on_err: E, ) -> ParseResult where F: FnOnce(&mut Self, &mut TokenIterator) -> ParseResult, E: FnOnce(&mut Self, &mut TokenIterator, ErrorInfo, TokenItem) -> ParseResult, { 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( &mut self, tokens: &mut TokenIterator, open: Token, close: Token, parse: F, ) -> ParseResult where F: FnOnce(&mut Self, &mut TokenIterator) -> ParseResult, Index: Into, // I: From, { 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(&mut self, tokens: &mut TokenIterator, parse: F) -> ParseResult where F: FnOnce(&mut Self, &mut TokenIterator) -> ParseResult, Index: Into, { self.parse_inner2( tokens, Token::OpenSquareBracket, Token::CloseSquareBracket, parse, ) } fn parse_braced(&mut self, tokens: &mut TokenIterator, parse: F) -> ParseResult where F: FnOnce(&mut Self, &mut TokenIterator) -> ParseResult, Index: Into, { self.parse_inner2(tokens, Token::OpenBrace, Token::CloseBrace, parse) } fn parse_parenthesised( &mut self, tokens: &mut TokenIterator, parse: F, ) -> ParseResult where F: FnOnce(&mut Self, &mut TokenIterator) -> ParseResult, Index: Into, { self.parse_inner2(tokens, Token::OpenParens, Token::CloseParens, parse) } fn parse_struct_fields( &mut self, tokens: &mut TokenIterator, ) -> ParseResult> { 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> { 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(&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(&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 { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_parameter(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_parameter_list( &mut self, ast: &mut Ast, idx: Index, ) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_block(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_block_trailing_expr( &mut self, ast: &mut Ast, idx: Index, ) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_block_maybe_trailing( &mut self, ast: &mut Ast, idx: Index, ) -> Result { 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 { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_call_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_add_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_sub_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_mul_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_div_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_rem_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_eq_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_neq_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_lt_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_gt_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_le_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_ge_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_shl_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_shr_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_bitor_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_bitxor_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_bitand_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_or_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_and_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_not_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_negate_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_deref_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_address_of_expr( &mut self, ast: &mut Ast, idx: Index, ) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_explicit_cast_expr( &mut self, ast: &mut Ast, idx: Index, ) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_assign(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_subscript_expr( &mut self, ast: &mut Ast, idx: Index, ) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_if_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_if_else_expr( &mut self, ast: &mut Ast, idx: Index, ) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_argument(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_any_argument( &mut self, ast: &mut Ast, idx: Index, ) -> Result { 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 { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_argument_list( &mut self, ast: &mut Ast, idx: Index, ) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_constant(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_return(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_return_expr(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_global_decl(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_var_decl(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_mut_var_decl( &mut self, ast: &mut Ast, idx: Index, ) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_var_assign_decl( &mut self, ast: &mut Ast, idx: Index, ) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_mut_var_assign_decl( &mut self, ast: &mut Ast, idx: Index, ) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_decl_ref(&mut self, ast: &mut Ast, idx: Index) -> Result { _ = (ast, idx); Err(Self::UNIMPL) } fn visit_value_to_place_conversion( &mut self, ast: &mut Ast, idx: Index, ) -> Result { 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 { 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 { 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 { 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, } struct Function { ir: IR, } struct IrFunctionBuilder<'a> { ir: &'a mut IR, type_cache: &'a mut TypeCache, ref_lookup: &'a mut HashMap, ip: &'a InternPool, function: Function, } impl<'a> AstVisitorTrait for IrFunctionBuilder<'a> { type Error = Error; type Value = Option; const UNIMPL: Self::Error = Error::Unimplemented; fn visit_block( &mut self, ast: &mut super::Ast, idx: super::Index, ) -> Result { 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 { 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 { 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 { 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.visit_var_decl_common(ast, idx) } fn visit_mut_var_decl( &mut self, ast: &mut super::Ast, idx: Index, ) -> Result { self.visit_var_decl_common(ast, idx) } fn visit_var_assign_decl( &mut self, ast: &mut super::Ast, idx: Index, ) -> Result { self.visit_var_decl_common(ast, idx) } fn visit_mut_var_assign_decl( &mut self, ast: &mut super::Ast, idx: Index, ) -> Result { self.visit_var_decl_common(ast, idx) } fn visit_return( &mut self, ast: &mut super::Ast, idx: Index, ) -> Result { _ = (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 { 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 { 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 { 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 { 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 { 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 { // todo!() // } fn visit_argument_list( &mut self, ast: &mut super::Ast, idx: Index, ) -> Result { 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::, _>>()?; 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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.visit_binop_expr(ast, idx) } fn visit_sub_expr( &mut self, ast: &mut super::Ast, idx: Index, ) -> Result { self.visit_binop_expr(ast, idx) } fn visit_div_expr( &mut self, ast: &mut super::Ast, idx: Index, ) -> Result { self.visit_binop_expr(ast, idx) } fn visit_rem_expr( &mut self, ast: &mut super::Ast, idx: Index, ) -> Result { self.visit_binop_expr(ast, idx) } fn visit_mul_expr( &mut self, ast: &mut super::Ast, idx: Index, ) -> Result { self.visit_binop_expr(ast, idx) } fn visit_bitand_expr( &mut self, ast: &mut super::Ast, idx: Index, ) -> Result { self.visit_binop_expr(ast, idx) } fn visit_bitor_expr( &mut self, ast: &mut super::Ast, idx: Index, ) -> Result { self.visit_binop_expr(ast, idx) } fn visit_bitxor_expr( &mut self, ast: &mut super::Ast, idx: Index, ) -> Result { self.visit_binop_expr(ast, idx) } fn visit_eq_expr( &mut self, ast: &mut super::Ast, idx: Index, ) -> Result { self.visit_binop_expr(ast, idx) } fn visit_neq_expr( &mut self, ast: &mut super::Ast, idx: Index, ) -> Result { self.visit_binop_expr(ast, idx) } fn visit_lt_expr( &mut self, ast: &mut super::Ast, idx: Index, ) -> Result { self.visit_binop_expr(ast, idx) } fn visit_gt_expr( &mut self, ast: &mut super::Ast, idx: Index, ) -> Result { self.visit_binop_expr(ast, idx) } fn visit_le_expr( &mut self, ast: &mut super::Ast, idx: Index, ) -> Result { self.visit_binop_expr(ast, idx) } fn visit_ge_expr( &mut self, ast: &mut super::Ast, idx: Index, ) -> Result { self.visit_binop_expr(ast, idx) } fn visit_shl_expr( &mut self, ast: &mut super::Ast, idx: Index, ) -> Result { self.visit_binop_expr(ast, idx) } fn visit_shr_expr( &mut self, ast: &mut super::Ast, idx: Index, ) -> Result { self.visit_binop_expr(ast, idx) } } impl IrFunctionBuilder<'_> { fn visit_var_decl_common( &mut self, ast: &mut super::Ast, idx: Index, ) -> Result, 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, 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::::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!(), } }, ); } } }