not really sure, i guess this is an IR?
This commit is contained in:
parent
02be9bdc26
commit
c3157b1355
139
src/ast.rs
139
src/ast.rs
|
@ -186,30 +186,30 @@ pub enum LetOrVar {
|
|||
Var,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct IntegralType {
|
||||
pub signed: bool,
|
||||
pub bits: u16,
|
||||
}
|
||||
|
||||
impl ToString for IntegralType {
|
||||
fn to_string(&self) -> String {
|
||||
format!("{}{}", if self.signed { "i" } else { "u" }, self.bits)
|
||||
impl core::fmt::Display for IntegralType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}{}", if self.signed { "i" } else { "u" }, self.bits)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum FloatingType {
|
||||
Binary32,
|
||||
Binary64,
|
||||
}
|
||||
|
||||
impl ToString for FloatingType {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
FloatingType::Binary32 => "binary32".to_owned(),
|
||||
FloatingType::Binary64 => "binary64".to_owned(),
|
||||
}
|
||||
impl core::fmt::Display for FloatingType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
FloatingType::Binary32 => "binary32",
|
||||
FloatingType::Binary64 => "binary64",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,6 +222,123 @@ impl IntegralType {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Type {
|
||||
Any,
|
||||
Void,
|
||||
Bool,
|
||||
Integer(IntegralType),
|
||||
Floating(FloatingType),
|
||||
Pointer {
|
||||
constness: bool,
|
||||
pointee: Box<Self>,
|
||||
},
|
||||
Fn {
|
||||
parameter_types: Vec<Self>,
|
||||
return_type: Box<Self>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn void() -> Type {
|
||||
Self::Void
|
||||
}
|
||||
pub fn bool() -> Type {
|
||||
Self::Void
|
||||
}
|
||||
pub fn any() -> Type {
|
||||
Self::Void
|
||||
}
|
||||
pub fn into_ptr(self) -> Type {
|
||||
Self::Pointer {
|
||||
constness: false,
|
||||
pointee: Box::new(self),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_logical_and_or(&self) -> bool {
|
||||
match self {
|
||||
Type::Bool => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_negate(&self) -> bool {
|
||||
match self {
|
||||
Type::Bool | Type::Integer(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_bitxor_and_or(&self) -> bool {
|
||||
match self {
|
||||
Type::Bool | Type::Integer(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_add_sub(&self) -> bool {
|
||||
match self {
|
||||
Type::Pointer { .. } | Type::Floating(_) | Type::Integer(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn can_shift(&self) -> bool {
|
||||
match self {
|
||||
Type::Integer(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn can_eq(&self) -> bool {
|
||||
match self {
|
||||
Type::Pointer { .. } | Type::Bool | Type::Floating(_) | Type::Integer(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn can_cmp(&self) -> bool {
|
||||
match self {
|
||||
Type::Pointer { .. } | Type::Floating(_) | Type::Integer(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn can_mul_div_rem(&self) -> bool {
|
||||
match self {
|
||||
Type::Floating(_) | Type::Integer(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_integer(&self) -> bool {
|
||||
match self {
|
||||
Type::Integer(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn remove_ptr(self) -> crate::parser::Result<Self> {
|
||||
match self {
|
||||
Type::Pointer { pointee, .. } => Ok(Box::into_inner(pointee)),
|
||||
_ => Err(crate::parser::Error::TriedToDerefNonPointer),
|
||||
}
|
||||
}
|
||||
pub fn align_of(&self) -> u32 {
|
||||
self.size_of()
|
||||
}
|
||||
pub fn size_of(&self) -> u32 {
|
||||
match self {
|
||||
Type::Any => 0,
|
||||
Type::Void => 0,
|
||||
Type::Bool => 1,
|
||||
Type::Integer(t) => t.bits.div_ceil(8) as u32,
|
||||
Type::Floating(t) => match t {
|
||||
FloatingType::Binary32 => 4,
|
||||
FloatingType::Binary64 => 8,
|
||||
},
|
||||
Type::Pointer { .. } => 8,
|
||||
Type::Fn { .. } => 8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PrimitiveType {
|
||||
FloatingType(FloatingType),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#![feature(extract_if, iter_advance_by)]
|
||||
#![feature(extract_if, iter_advance_by, box_into_inner)]
|
||||
#![allow(dead_code, unused_macros)]
|
||||
|
||||
pub mod ast;
|
||||
|
@ -6,6 +6,7 @@ pub mod common;
|
|||
pub mod lexer;
|
||||
pub mod parser;
|
||||
pub mod tokens;
|
||||
pub mod triples;
|
||||
|
||||
pub fn tokenize<'a>(
|
||||
bytes: &'a [u8],
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::collections::HashMap;
|
|||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
ast::{FloatingType, IntegralType, LetOrVar, Node, PrimitiveType, Tag},
|
||||
ast::{FloatingType, IntegralType, LetOrVar, Node, PrimitiveType, Tag, Type},
|
||||
common::NextIf,
|
||||
lexer::{Radix, TokenIterator},
|
||||
tokens::Token,
|
||||
|
@ -21,17 +21,22 @@ pub enum Error {
|
|||
ExpectedLetOrVar,
|
||||
#[error("Dummy message.")]
|
||||
IntegralTypeTooWide,
|
||||
#[error("Dummy message.")]
|
||||
TriedToDerefNonPointer,
|
||||
}
|
||||
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
|
||||
// TODO: add a string-table which stores strings and maybe other bytes and
|
||||
// returns a range for identifiers, constants, etc. where bytes are stored
|
||||
// flatly, and next to each other.
|
||||
#[derive(Debug)]
|
||||
pub struct Tree {
|
||||
nodes: Vec<Tag>,
|
||||
global_decls: Vec<Node>,
|
||||
pub global_decls: Vec<Node>,
|
||||
}
|
||||
|
||||
fn write_indented_inner<W: core::fmt::Write>(
|
||||
pub fn write_indented_inner<W: core::fmt::Write>(
|
||||
dst: &mut W,
|
||||
indent: u32,
|
||||
nl: bool,
|
||||
|
@ -47,15 +52,17 @@ fn write_indented_inner<W: core::fmt::Write>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! write_indented {
|
||||
($indent:expr, $w:expr, $($arg:tt)*) => {
|
||||
write_indented_inner($w, $indent, false, format_args!($($arg)*))
|
||||
$crate::parser::write_indented_inner($w, $indent, false, format_args!($($arg)*))
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! writeln_indented {
|
||||
($indent:expr, $w:expr, $($arg:tt)*) => {
|
||||
write_indented_inner($w, $indent, true, format_args!($($arg)*))
|
||||
$crate::parser::write_indented_inner($w, $indent, true, format_args!($($arg)*))
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -73,18 +80,16 @@ impl Tree {
|
|||
node
|
||||
}
|
||||
|
||||
fn set_node(&mut self, node: Node, tag: Tag) -> Option<()> {
|
||||
*self.get_node_mut(node)? = tag;
|
||||
|
||||
Some(())
|
||||
fn set_node(&mut self, node: Node, tag: Tag) {
|
||||
*self.get_node_mut(node) = tag;
|
||||
}
|
||||
|
||||
fn get_node_mut(&mut self, node: Node) -> Option<&mut Tag> {
|
||||
self.nodes.get_mut(node.get() as usize)
|
||||
fn get_node_mut(&mut self, node: Node) -> &mut Tag {
|
||||
self.nodes.get_mut(node.get() as usize).unwrap()
|
||||
}
|
||||
|
||||
fn get_node(&self, node: Node) -> Option<&Tag> {
|
||||
self.nodes.get(node.get() as usize)
|
||||
pub fn get_node(&self, node: Node) -> &Tag {
|
||||
self.nodes.get(node.get() as usize).unwrap()
|
||||
}
|
||||
|
||||
fn push_tag(&mut self, tag: Tag) -> Node {
|
||||
|
@ -301,10 +306,10 @@ impl Tree {
|
|||
explicit_type,
|
||||
assignment,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
);
|
||||
|
||||
Ok(node)
|
||||
// return assignment if it exists, to make rendering and visiting easier
|
||||
Ok(assignment.unwrap_or(node))
|
||||
}
|
||||
|
||||
pub fn parse_global_decl(&mut self, tokens: &mut TokenIterator) -> Result<Node> {
|
||||
|
@ -726,7 +731,7 @@ impl Tree {
|
|||
self.parse_program(&mut tokens)
|
||||
}
|
||||
|
||||
fn get_ident_str(&self, node: Node) -> Option<&str> {
|
||||
pub fn get_ident_str(&self, node: Node) -> Option<&str> {
|
||||
match &self.nodes[node.get() as usize] {
|
||||
Tag::Ident { name } => Some(name.as_str()),
|
||||
_ => None,
|
||||
|
@ -734,7 +739,7 @@ impl Tree {
|
|||
}
|
||||
|
||||
fn get_typename_str(&self, node: Node) -> Option<String> {
|
||||
match self.get_node(node)? {
|
||||
match self.get_node(node) {
|
||||
Tag::IntegralType(i) => Some(i.to_string()),
|
||||
Tag::Ident { name } => Some(name.clone()),
|
||||
Tag::Pointer { pointee } => self.get_typename_str(*pointee),
|
||||
|
@ -769,7 +774,7 @@ impl Tree {
|
|||
writeln!(writer, "}}")
|
||||
}
|
||||
Tag::ParameterList { parameters } => {
|
||||
writeln_indented!(indent, writer, "%{} = ParameterList = [", node.get())?;
|
||||
writeln_indented!(indent, writer, "%{} = ParameterList [", node.get())?;
|
||||
for param in parameters {
|
||||
self.render_node(writer, *param, indent + 1)?;
|
||||
}
|
||||
|
@ -864,7 +869,7 @@ impl Tree {
|
|||
let_or_var,
|
||||
name,
|
||||
explicit_type,
|
||||
assignment,
|
||||
..
|
||||
} => {
|
||||
self.render_node(writer, *name, indent)?;
|
||||
explicit_type.map(|ty| self.render_node(writer, ty, indent));
|
||||
|
@ -887,9 +892,6 @@ impl Tree {
|
|||
write!(writer, ", ty: {}", self.get_typename_str(*ty).unwrap())?;
|
||||
}
|
||||
writeln!(writer, ");")?;
|
||||
if let Some(assignment) = assignment {
|
||||
self.render_node(writer, *assignment, indent)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Tag::CallExpr { lhs, rhs } => todo!(),
|
||||
|
@ -1145,6 +1147,7 @@ impl Tree {
|
|||
)
|
||||
}
|
||||
Tag::Assign { lhs, rhs } => {
|
||||
self.render_node(writer, *lhs, indent)?;
|
||||
self.render_node(writer, *rhs, indent)?;
|
||||
writeln_indented!(
|
||||
indent,
|
||||
|
|
608
src/triples.rs
Normal file
608
src/triples.rs
Normal file
|
@ -0,0 +1,608 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use std::{collections::HashMap, marker::PhantomData, num::NonZero};
|
||||
|
||||
use crate::{
|
||||
ast::{FloatingType, IntegralType, Node as AstNode, PrimitiveType, Tag, Type},
|
||||
parser::Tree,
|
||||
writeln_indented,
|
||||
};
|
||||
|
||||
struct SymbolRecord {
|
||||
name: String,
|
||||
decl: AstNode,
|
||||
ty: Type,
|
||||
}
|
||||
|
||||
struct SymbolTable<'a> {
|
||||
tree: &'a Tree,
|
||||
identifiers: Vec<SymbolRecord>,
|
||||
parent: Option<Box<SymbolTable<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a> SymbolTable<'a> {
|
||||
pub fn root(tree: &'a Tree) -> Self {
|
||||
Self {
|
||||
tree,
|
||||
identifiers: Vec::new(),
|
||||
parent: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_symbol_with_name(&mut self, ast_node: AstNode, name: String) {
|
||||
let ty = self.type_of_node(ast_node);
|
||||
self.identifiers.push(SymbolRecord {
|
||||
name,
|
||||
decl: ast_node,
|
||||
ty,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn insert_return_symbol(&mut self, ast_node: AstNode) {
|
||||
self.insert_symbol_with_name(ast_node, "return".to_string())
|
||||
}
|
||||
|
||||
pub fn insert_symbol(&mut self, ast_node: AstNode) {
|
||||
let name = match self.tree.get_node(ast_node) {
|
||||
Tag::VarDecl { name, .. } => self.tree.get_ident_str(*name).unwrap().to_string(),
|
||||
Tag::Parameter { name, .. } => self.tree.get_ident_str(*name).unwrap().to_string(),
|
||||
Tag::FunctionProto { name, .. } => self.tree.get_ident_str(*name).unwrap().to_string(),
|
||||
_ => {
|
||||
panic!("ast_node wasn't any kind of decl!");
|
||||
}
|
||||
};
|
||||
|
||||
self.insert_symbol_with_name(ast_node, name)
|
||||
}
|
||||
|
||||
pub fn find_symbol_name(&self, name: &str) -> Option<&SymbolRecord> {
|
||||
self.identifiers
|
||||
.iter()
|
||||
.find(|r| r.name.as_str() == name)
|
||||
.or_else(|| {
|
||||
self.parent
|
||||
.as_ref()
|
||||
.and_then(|parent| parent.find_symbol_name(name))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn find_symbol(&self, ident_node: AstNode) -> Option<&SymbolRecord> {
|
||||
self.identifiers
|
||||
.iter()
|
||||
.find(|r| Some(r.name.as_str()) == self.tree.get_ident_str(ident_node))
|
||||
.or_else(|| {
|
||||
self.parent
|
||||
.as_ref()
|
||||
.and_then(|parent| parent.find_symbol(ident_node))
|
||||
})
|
||||
}
|
||||
pub fn into_child_in_place(&mut self) {
|
||||
let mut parent = Self {
|
||||
parent: None,
|
||||
tree: self.tree,
|
||||
identifiers: Vec::new(),
|
||||
};
|
||||
core::mem::swap(self, &mut parent);
|
||||
self.parent = Some(Box::new(parent));
|
||||
}
|
||||
|
||||
pub fn into_child(self) -> SymbolTable<'a> {
|
||||
Self {
|
||||
identifiers: Vec::new(),
|
||||
tree: self.tree,
|
||||
parent: Some(Box::new(self)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parent_mut(&mut self) -> Option<&mut SymbolTable<'a>> {
|
||||
self.parent.as_mut().map(|parent| parent.as_mut())
|
||||
}
|
||||
|
||||
pub fn into_parent_in_place(&mut self) -> Option<SymbolTable<'a>> {
|
||||
if let Some(child) = self.parent.take() {
|
||||
let mut child = Box::into_inner(child);
|
||||
core::mem::swap(self, &mut child);
|
||||
Some(child)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_parent(self) -> Option<SymbolTable<'a>> {
|
||||
self.parent.map(|parent| Box::into_inner(parent))
|
||||
}
|
||||
|
||||
fn type_of_node(&self, node: AstNode) -> crate::ast::Type {
|
||||
match self.tree.get_node(node) {
|
||||
Tag::Ident { name } => self
|
||||
.find_symbol(node)
|
||||
.map(|r| r.ty.clone())
|
||||
.expect(&format!("identifier '{name}' not found in SymbolTable!")),
|
||||
Tag::FunctionDecl { proto, .. } => self.type_of_node(*proto),
|
||||
Tag::FunctionProto {
|
||||
parameters,
|
||||
return_type,
|
||||
..
|
||||
} => {
|
||||
let return_type = self.type_of_node(*return_type);
|
||||
let parameter_types = parameters
|
||||
.map(|p| match self.tree.get_node(p) {
|
||||
Tag::ParameterList { parameters } => parameters
|
||||
.iter()
|
||||
.map(|p| self.type_of_node(*p))
|
||||
.collect::<Vec<_>>(),
|
||||
_ => panic!("parameters is not a parameterlist!"),
|
||||
})
|
||||
.unwrap_or(Vec::new());
|
||||
|
||||
crate::ast::Type::Fn {
|
||||
parameter_types,
|
||||
return_type: Box::new(return_type),
|
||||
}
|
||||
}
|
||||
Tag::Parameter { ty, .. } => self.type_of_node(*ty),
|
||||
Tag::Pointer { pointee } => Type::Pointer {
|
||||
constness: false,
|
||||
pointee: Box::new(self.type_of_node(*pointee)),
|
||||
},
|
||||
Tag::IntegralType(t) => Type::Integer(*t),
|
||||
Tag::PrimitiveType(t) => match t {
|
||||
PrimitiveType::FloatingType(t) => Type::Floating(*t),
|
||||
PrimitiveType::IntegralType(t) => self.type_of_node(*t),
|
||||
PrimitiveType::Bool => Type::bool(),
|
||||
PrimitiveType::Void => Type::void(),
|
||||
},
|
||||
Tag::IntegralConstant { ty, .. } => Type::Integer(*ty),
|
||||
Tag::FloatingConstant { ty, .. } => Type::Floating(*ty),
|
||||
Tag::Block { trailing_expr, .. } => trailing_expr
|
||||
.map(|n| self.type_of_node(n))
|
||||
.unwrap_or(Type::void()),
|
||||
Tag::VarDecl {
|
||||
explicit_type,
|
||||
assignment, // this is a Tag::Assign
|
||||
..
|
||||
} => {
|
||||
let lhs = explicit_type.map(|n| self.type_of_node(n));
|
||||
let rhs = assignment.map(|n| match self.tree.get_node(n) {
|
||||
Tag::Assign { rhs, .. } => self.type_of_node(*rhs),
|
||||
_ => unreachable!(),
|
||||
});
|
||||
if lhs.as_ref().zip(rhs.as_ref()).map(|(l, r)| l != r) == Some(true) {
|
||||
eprintln!("vardecl: incompatible types {lhs:?} and {rhs:?}.");
|
||||
}
|
||||
lhs.or(rhs)
|
||||
.expect("Type could not be automatically deduced.")
|
||||
}
|
||||
Tag::CallExpr { lhs, .. } => self.type_of_node(*lhs),
|
||||
Tag::ExplicitCast { typename, .. } => self.type_of_node(*typename),
|
||||
Tag::Deref { lhs } => self.type_of_node(*lhs).remove_ptr().unwrap(),
|
||||
Tag::Ref { lhs } => self.type_of_node(*lhs).into_ptr(),
|
||||
Tag::Not { lhs } => self.type_of_node(*lhs),
|
||||
Tag::Negate { lhs } => self.type_of_node(*lhs),
|
||||
Tag::Or { lhs, .. } => self.type_of_node(*lhs),
|
||||
Tag::And { lhs, .. } => self.type_of_node(*lhs),
|
||||
Tag::BitOr { lhs, .. } => self.type_of_node(*lhs),
|
||||
Tag::BitAnd { lhs, .. } => self.type_of_node(*lhs),
|
||||
Tag::BitXOr { lhs, .. } => self.type_of_node(*lhs),
|
||||
Tag::Shl { lhs, .. } => self.type_of_node(*lhs),
|
||||
Tag::Shr { lhs, .. } => self.type_of_node(*lhs),
|
||||
Tag::Add { lhs, .. } => self.type_of_node(*lhs),
|
||||
Tag::Sub { lhs, .. } => self.type_of_node(*lhs),
|
||||
Tag::Mul { lhs, .. } => self.type_of_node(*lhs),
|
||||
Tag::Rem { lhs, .. } => self.type_of_node(*lhs),
|
||||
Tag::Div { lhs, .. } => self.type_of_node(*lhs),
|
||||
Tag::Eq { .. } => Type::bool(),
|
||||
Tag::NEq { .. } => Type::bool(),
|
||||
Tag::Lt { .. } => Type::bool(),
|
||||
Tag::Gt { .. } => Type::bool(),
|
||||
Tag::Le { .. } => Type::bool(),
|
||||
Tag::Ge { .. } => Type::bool(),
|
||||
_ => Type::void(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Node = u32;
|
||||
|
||||
enum Inst {
|
||||
Label(String),
|
||||
Constant(Value),
|
||||
Parameter,
|
||||
Add { lhs: Node, rhs: Node },
|
||||
Sub { lhs: Node, rhs: Node },
|
||||
Div { lhs: Node, rhs: Node },
|
||||
Mul { lhs: Node, rhs: Node },
|
||||
Rem { lhs: Node, rhs: Node },
|
||||
BitAnd { lhs: Node, rhs: Node },
|
||||
BitOr { lhs: Node, rhs: Node },
|
||||
BitXOr { lhs: Node, rhs: Node },
|
||||
Negate { lhs: Node },
|
||||
ReturnValue { lhs: Node },
|
||||
Return,
|
||||
Alloc { size: u32, align: u32 },
|
||||
AddressOf(Node),
|
||||
Load { source: Node },
|
||||
Store { dest: Node, source: Node },
|
||||
}
|
||||
|
||||
enum Value {
|
||||
Int { kind: IntegralType, bits: u64 },
|
||||
Float { kind: FloatingType, bits: u64 },
|
||||
}
|
||||
|
||||
impl core::fmt::Display for Value {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Value::Int { kind, bits } => {
|
||||
write!(f, "{kind} {}", bits)
|
||||
}
|
||||
Value::Float { kind, bits } => {
|
||||
write!(f, "{kind} {{{}}}", bits)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct IRBuilder<'tree, 'ir> {
|
||||
ir: &'ir mut IR,
|
||||
st: SymbolTable<'tree>,
|
||||
lookup: HashMap<AstNode, Node>,
|
||||
}
|
||||
|
||||
impl<'tree, 'ir> IRBuilder<'tree, 'ir> {
|
||||
fn new(ir: &'ir mut IR, st: SymbolTable<'tree>) -> Self {
|
||||
Self {
|
||||
ir,
|
||||
st,
|
||||
lookup: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit(&mut self, node: AstNode) -> Node {
|
||||
println!("visiting %{}", node.get());
|
||||
match self.st.tree.get_node(node) {
|
||||
Tag::FunctionDecl { proto, body } => {
|
||||
self.visit(*proto);
|
||||
self.st.into_child_in_place();
|
||||
let value = self.visit(*body);
|
||||
// TODO: return value of body expression
|
||||
let node = if value != !0 {
|
||||
self.type_check(self.st.find_symbol_name("return").unwrap().decl, *body);
|
||||
self.ir.push(Inst::ReturnValue { lhs: value })
|
||||
} else {
|
||||
!0
|
||||
};
|
||||
|
||||
self.st.into_parent_in_place();
|
||||
node
|
||||
}
|
||||
Tag::FunctionProto {
|
||||
parameters,
|
||||
return_type,
|
||||
name,
|
||||
} => {
|
||||
self.st.insert_symbol(node);
|
||||
self.st.insert_return_symbol(*return_type);
|
||||
parameters.map(|p| self.visit(p));
|
||||
|
||||
let label = self.ir.push(Inst::Label(
|
||||
self.st.tree.get_ident_str(*name).unwrap().to_string(),
|
||||
));
|
||||
|
||||
self.lookup.insert(node, label);
|
||||
|
||||
label
|
||||
}
|
||||
Tag::ParameterList { parameters } => {
|
||||
for param in parameters {
|
||||
self.visit(*param);
|
||||
}
|
||||
!0
|
||||
}
|
||||
Tag::Parameter { .. } => {
|
||||
self.st.insert_symbol(node);
|
||||
let param = self.ir.push(Inst::Parameter);
|
||||
|
||||
self.lookup.insert(node, param);
|
||||
param
|
||||
}
|
||||
Tag::Block {
|
||||
statements,
|
||||
trailing_expr,
|
||||
} => {
|
||||
for stmt in statements {
|
||||
self.visit(*stmt);
|
||||
}
|
||||
|
||||
if let Some(expr) = trailing_expr {
|
||||
self.visit(*expr)
|
||||
} else {
|
||||
!0
|
||||
}
|
||||
}
|
||||
Tag::VarDecl { .. } => {
|
||||
let ty = self.st.type_of_node(node);
|
||||
let alloca = self.ir.push(Inst::Alloc {
|
||||
size: ty.size_of(),
|
||||
align: ty.align_of(),
|
||||
});
|
||||
self.st.insert_symbol(node);
|
||||
self.lookup.insert(node, alloca);
|
||||
alloca
|
||||
}
|
||||
Tag::ReturnStmt { expr } => {
|
||||
if let Some(expr) = expr {
|
||||
let expr = self.visit(*expr);
|
||||
self.ir.push(Inst::ReturnValue { lhs: expr })
|
||||
} else {
|
||||
self.ir.push(Inst::Return)
|
||||
}
|
||||
}
|
||||
Tag::ExprStmt { expr } => self.visit(*expr),
|
||||
Tag::Deref { lhs } => {
|
||||
let lhs = self.visit(*lhs);
|
||||
self.ir.push(Inst::Load { source: lhs })
|
||||
}
|
||||
Tag::IntegralConstant { bits, ty } => self.ir.push(Inst::Constant(Value::Int {
|
||||
kind: *ty,
|
||||
bits: *bits,
|
||||
})),
|
||||
Tag::FloatingConstant { bits, ty } => self.ir.push(Inst::Constant(Value::Float {
|
||||
kind: *ty,
|
||||
bits: *bits,
|
||||
})),
|
||||
Tag::Assign { lhs, rhs } => {
|
||||
self.type_check(*lhs, *rhs);
|
||||
|
||||
let lhs = self.visit(*lhs);
|
||||
let rhs = self.visit(*rhs);
|
||||
self.ir.push(Inst::Store {
|
||||
dest: lhs,
|
||||
source: rhs,
|
||||
})
|
||||
}
|
||||
Tag::Add { lhs, rhs } => {
|
||||
let ty = self.type_check(*lhs, *rhs);
|
||||
if !ty.can_add_sub() {
|
||||
eprintln!("add is not available for type {ty:?}");
|
||||
}
|
||||
|
||||
let lhs = self.visit(*lhs);
|
||||
let rhs = self.visit(*rhs);
|
||||
self.ir.push(Inst::Add { lhs, rhs })
|
||||
}
|
||||
Tag::Sub { lhs, rhs } => {
|
||||
let ty = self.type_check(*lhs, *rhs);
|
||||
if !ty.can_add_sub() {
|
||||
eprintln!("sub is not available for type {ty:?}");
|
||||
}
|
||||
|
||||
let lhs = self.visit(*lhs);
|
||||
let rhs = self.visit(*rhs);
|
||||
self.ir.push(Inst::Sub { lhs, rhs })
|
||||
}
|
||||
Tag::Mul { lhs, rhs } => {
|
||||
let ty = self.type_check(*lhs, *rhs);
|
||||
if !ty.can_mul_div_rem() {
|
||||
eprintln!("mul is not available for type {ty:?}");
|
||||
}
|
||||
|
||||
let lhs = self.visit(*lhs);
|
||||
let rhs = self.visit(*rhs);
|
||||
self.ir.push(Inst::Mul { lhs, rhs })
|
||||
}
|
||||
Tag::Div { lhs, rhs } => {
|
||||
let ty = self.type_check(*lhs, *rhs);
|
||||
if !ty.can_mul_div_rem() {
|
||||
eprintln!("div is not available for type {ty:?}");
|
||||
}
|
||||
|
||||
let lhs = self.visit(*lhs);
|
||||
let rhs = self.visit(*rhs);
|
||||
self.ir.push(Inst::Div { lhs, rhs })
|
||||
}
|
||||
Tag::Rem { lhs, rhs } => {
|
||||
let ty = self.type_check(*lhs, *rhs);
|
||||
if !ty.can_mul_div_rem() {
|
||||
eprintln!("rem is not available for type {ty:?}");
|
||||
}
|
||||
|
||||
let lhs = self.visit(*lhs);
|
||||
let rhs = self.visit(*rhs);
|
||||
self.ir.push(Inst::Rem { lhs, rhs })
|
||||
}
|
||||
Tag::BitAnd { lhs, rhs } => {
|
||||
let ty = self.type_check(*lhs, *rhs);
|
||||
if !ty.can_bitxor_and_or() {
|
||||
eprintln!("bitand is not available for type {ty:?}");
|
||||
}
|
||||
|
||||
let lhs = self.visit(*lhs);
|
||||
let rhs = self.visit(*rhs);
|
||||
self.ir.push(Inst::BitAnd { lhs, rhs })
|
||||
}
|
||||
Tag::BitOr { lhs, rhs } => {
|
||||
let ty = self.type_check(*lhs, *rhs);
|
||||
if !ty.can_bitxor_and_or() {
|
||||
eprintln!("bitor is not available for type {ty:?}");
|
||||
}
|
||||
|
||||
let lhs = self.visit(*lhs);
|
||||
let rhs = self.visit(*rhs);
|
||||
self.ir.push(Inst::BitOr { lhs, rhs })
|
||||
}
|
||||
Tag::BitXOr { lhs, rhs } => {
|
||||
let ty = self.type_check(*lhs, *rhs);
|
||||
if !ty.can_bitxor_and_or() {
|
||||
eprintln!("bitxor is not available for type {ty:?}");
|
||||
}
|
||||
|
||||
let lhs = self.visit(*lhs);
|
||||
let rhs = self.visit(*rhs);
|
||||
self.ir.push(Inst::BitXOr { lhs, rhs })
|
||||
}
|
||||
Tag::Negate { lhs } => {
|
||||
let ty = self.st.type_of_node(*lhs);
|
||||
if !ty.can_negate() {
|
||||
eprintln!("negation is not available for type {ty:?}");
|
||||
}
|
||||
|
||||
let lhs = self.visit(*lhs);
|
||||
self.ir.push(Inst::Negate { lhs })
|
||||
}
|
||||
Tag::Ident { name } => {
|
||||
let decl = self
|
||||
.st
|
||||
.find_symbol(node)
|
||||
.expect(&format!("symbol '{name}' not found in SymbolMap!"))
|
||||
.decl;
|
||||
|
||||
*self.lookup.get(&decl).unwrap()
|
||||
}
|
||||
Tag::Ref { lhs } => {
|
||||
let lhs = self.visit(*lhs);
|
||||
self.ir.push(Inst::AddressOf(lhs))
|
||||
}
|
||||
_ => {
|
||||
dbg!(self.st.tree.get_node(node));
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn type_check(&self, lhs: AstNode, rhs: AstNode) -> Type {
|
||||
let t_lhs = self.st.type_of_node(lhs);
|
||||
let t_rhs = self.st.type_of_node(rhs);
|
||||
if t_lhs != t_rhs {
|
||||
eprintln!("incompatible types {t_lhs:?} and {t_rhs:?}!");
|
||||
}
|
||||
t_lhs
|
||||
}
|
||||
}
|
||||
|
||||
struct IR {
|
||||
nodes: Vec<Inst>,
|
||||
}
|
||||
|
||||
impl IR {
|
||||
pub fn new() -> Self {
|
||||
Self { nodes: Vec::new() }
|
||||
}
|
||||
|
||||
fn push(&mut self, inst: Inst) -> u32 {
|
||||
let node = self.nodes.len() as u32;
|
||||
self.nodes.push(inst);
|
||||
node
|
||||
}
|
||||
|
||||
pub fn build(&mut self, tree: &Tree, ast_node: crate::ast::Node) {
|
||||
let st = SymbolTable::root(tree);
|
||||
let mut builder = IRBuilder::new(self, st);
|
||||
|
||||
builder.visit(ast_node);
|
||||
}
|
||||
|
||||
fn render_node<W: core::fmt::Write>(
|
||||
&self,
|
||||
w: &mut W,
|
||||
node: Node,
|
||||
indent: u32,
|
||||
) -> core::fmt::Result {
|
||||
match &self.nodes[node as usize] {
|
||||
Inst::Label(label) => {
|
||||
writeln_indented!(indent - 1, w, "%{} = {label}:", node)?;
|
||||
}
|
||||
Inst::Parameter => {
|
||||
writeln_indented!(indent, w, "%{} = Param", node)?;
|
||||
}
|
||||
Inst::Constant(value) => {
|
||||
writeln_indented!(indent, w, "%{} = {}", node, value)?;
|
||||
}
|
||||
Inst::Add { lhs, rhs } => {
|
||||
writeln_indented!(indent, w, "%{} = %{} + %{}", node, lhs, rhs)?;
|
||||
}
|
||||
Inst::Sub { lhs, rhs } => {
|
||||
writeln_indented!(indent, w, "%{} = %{} - %{}", node, lhs, rhs)?;
|
||||
}
|
||||
Inst::Mul { lhs, rhs } => {
|
||||
writeln_indented!(indent, w, "%{} = %{} * %{}", node, lhs, rhs)?;
|
||||
}
|
||||
Inst::Div { lhs, rhs } => {
|
||||
writeln_indented!(indent, w, "%{} = %{} / %{}", node, lhs, rhs)?;
|
||||
}
|
||||
Inst::Rem { lhs, rhs } => {
|
||||
writeln_indented!(indent, w, "%{} = %{} % %{}", node, lhs, rhs)?;
|
||||
}
|
||||
Inst::BitAnd { lhs, rhs } => {
|
||||
writeln_indented!(indent, w, "%{} = %{} & %{}", node, lhs, rhs)?;
|
||||
}
|
||||
Inst::BitOr { lhs, rhs } => {
|
||||
writeln_indented!(indent, w, "%{} = %{} | %{}", node, lhs, rhs)?;
|
||||
}
|
||||
Inst::BitXOr { lhs, rhs } => {
|
||||
writeln_indented!(indent, w, "%{} = %{} ^ %{}", node, lhs, rhs)?;
|
||||
}
|
||||
Inst::Negate { lhs } => {
|
||||
writeln_indented!(indent, w, "%{} = !%{}", node, lhs)?;
|
||||
}
|
||||
Inst::ReturnValue { lhs } => {
|
||||
writeln_indented!(indent, w, "%{} = return %{}", node, lhs)?;
|
||||
}
|
||||
Inst::Return => {
|
||||
writeln_indented!(indent, w, "%{} = return", node)?;
|
||||
}
|
||||
Inst::Alloc { size, align } => {
|
||||
writeln_indented!(indent, w, "%{} = alloca {size} (algin: {align})", node)?;
|
||||
}
|
||||
Inst::AddressOf(val) => {
|
||||
writeln_indented!(indent, w, "%{} = addr %{val}", node)?;
|
||||
}
|
||||
Inst::Load { source } => {
|
||||
writeln_indented!(indent, w, "%{} = load ptr %{source}", node)?;
|
||||
}
|
||||
Inst::Store { dest, source } => {
|
||||
writeln_indented!(indent, w, "%{} = store ptr %{dest} from %{source}", node)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn render<W: core::fmt::Write>(&self, w: &mut W) -> core::fmt::Result {
|
||||
for node in 0..self.nodes.len() {
|
||||
self.render_node(w, node as u32, 1)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::lexer::Tokenizer;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn ir() {
|
||||
let src = "
|
||||
fn main() -> u32 {
|
||||
let a: u32 = 0 + 3;
|
||||
let ptr_a = &a;
|
||||
return *ptr_a * 2;
|
||||
}
|
||||
";
|
||||
let tokens = Tokenizer::new(src.as_bytes()).unwrap();
|
||||
|
||||
let mut tree = Tree::new();
|
||||
tree.parse(tokens.iter()).unwrap();
|
||||
|
||||
let mut buf = String::new();
|
||||
tree.render(&mut buf).unwrap();
|
||||
println!("{buf}");
|
||||
|
||||
let mut ir = IR::new();
|
||||
ir.build(&tree, *tree.global_decls.first().unwrap());
|
||||
let mut buf = String::new();
|
||||
ir.render(&mut buf).unwrap();
|
||||
println!("{buf}");
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue