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,
|
Var,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct IntegralType {
|
pub struct IntegralType {
|
||||||
pub signed: bool,
|
pub signed: bool,
|
||||||
pub bits: u16,
|
pub bits: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for IntegralType {
|
impl core::fmt::Display for IntegralType {
|
||||||
fn to_string(&self) -> String {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
format!("{}{}", if self.signed { "i" } else { "u" }, self.bits)
|
write!(f, "{}{}", if self.signed { "i" } else { "u" }, self.bits)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum FloatingType {
|
pub enum FloatingType {
|
||||||
Binary32,
|
Binary32,
|
||||||
Binary64,
|
Binary64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for FloatingType {
|
impl core::fmt::Display for FloatingType {
|
||||||
fn to_string(&self) -> String {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
f.write_str(match self {
|
||||||
FloatingType::Binary32 => "binary32".to_owned(),
|
FloatingType::Binary32 => "binary32",
|
||||||
FloatingType::Binary64 => "binary64".to_owned(),
|
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)]
|
#[derive(Debug)]
|
||||||
pub enum PrimitiveType {
|
pub enum PrimitiveType {
|
||||||
FloatingType(FloatingType),
|
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)]
|
#![allow(dead_code, unused_macros)]
|
||||||
|
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
|
@ -6,6 +6,7 @@ pub mod common;
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod tokens;
|
pub mod tokens;
|
||||||
|
pub mod triples;
|
||||||
|
|
||||||
pub fn tokenize<'a>(
|
pub fn tokenize<'a>(
|
||||||
bytes: &'a [u8],
|
bytes: &'a [u8],
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::collections::HashMap;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{FloatingType, IntegralType, LetOrVar, Node, PrimitiveType, Tag},
|
ast::{FloatingType, IntegralType, LetOrVar, Node, PrimitiveType, Tag, Type},
|
||||||
common::NextIf,
|
common::NextIf,
|
||||||
lexer::{Radix, TokenIterator},
|
lexer::{Radix, TokenIterator},
|
||||||
tokens::Token,
|
tokens::Token,
|
||||||
|
@ -21,17 +21,22 @@ pub enum Error {
|
||||||
ExpectedLetOrVar,
|
ExpectedLetOrVar,
|
||||||
#[error("Dummy message.")]
|
#[error("Dummy message.")]
|
||||||
IntegralTypeTooWide,
|
IntegralTypeTooWide,
|
||||||
|
#[error("Dummy message.")]
|
||||||
|
TriedToDerefNonPointer,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = core::result::Result<T, Error>;
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct Tree {
|
pub struct Tree {
|
||||||
nodes: Vec<Tag>,
|
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,
|
dst: &mut W,
|
||||||
indent: u32,
|
indent: u32,
|
||||||
nl: bool,
|
nl: bool,
|
||||||
|
@ -47,15 +52,17 @@ fn write_indented_inner<W: core::fmt::Write>(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
macro_rules! write_indented {
|
macro_rules! write_indented {
|
||||||
($indent:expr, $w:expr, $($arg:tt)*) => {
|
($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 {
|
macro_rules! writeln_indented {
|
||||||
($indent:expr, $w:expr, $($arg:tt)*) => {
|
($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
|
node
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_node(&mut self, node: Node, tag: Tag) -> Option<()> {
|
fn set_node(&mut self, node: Node, tag: Tag) {
|
||||||
*self.get_node_mut(node)? = tag;
|
*self.get_node_mut(node) = tag;
|
||||||
|
|
||||||
Some(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_node_mut(&mut self, node: Node) -> Option<&mut Tag> {
|
fn get_node_mut(&mut self, node: Node) -> &mut Tag {
|
||||||
self.nodes.get_mut(node.get() as usize)
|
self.nodes.get_mut(node.get() as usize).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_node(&self, node: Node) -> Option<&Tag> {
|
pub fn get_node(&self, node: Node) -> &Tag {
|
||||||
self.nodes.get(node.get() as usize)
|
self.nodes.get(node.get() as usize).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_tag(&mut self, tag: Tag) -> Node {
|
fn push_tag(&mut self, tag: Tag) -> Node {
|
||||||
|
@ -301,10 +306,10 @@ impl Tree {
|
||||||
explicit_type,
|
explicit_type,
|
||||||
assignment,
|
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> {
|
pub fn parse_global_decl(&mut self, tokens: &mut TokenIterator) -> Result<Node> {
|
||||||
|
@ -726,7 +731,7 @@ impl Tree {
|
||||||
self.parse_program(&mut tokens)
|
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] {
|
match &self.nodes[node.get() as usize] {
|
||||||
Tag::Ident { name } => Some(name.as_str()),
|
Tag::Ident { name } => Some(name.as_str()),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -734,7 +739,7 @@ impl Tree {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_typename_str(&self, node: Node) -> Option<String> {
|
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::IntegralType(i) => Some(i.to_string()),
|
||||||
Tag::Ident { name } => Some(name.clone()),
|
Tag::Ident { name } => Some(name.clone()),
|
||||||
Tag::Pointer { pointee } => self.get_typename_str(*pointee),
|
Tag::Pointer { pointee } => self.get_typename_str(*pointee),
|
||||||
|
@ -769,7 +774,7 @@ impl Tree {
|
||||||
writeln!(writer, "}}")
|
writeln!(writer, "}}")
|
||||||
}
|
}
|
||||||
Tag::ParameterList { parameters } => {
|
Tag::ParameterList { parameters } => {
|
||||||
writeln_indented!(indent, writer, "%{} = ParameterList = [", node.get())?;
|
writeln_indented!(indent, writer, "%{} = ParameterList [", node.get())?;
|
||||||
for param in parameters {
|
for param in parameters {
|
||||||
self.render_node(writer, *param, indent + 1)?;
|
self.render_node(writer, *param, indent + 1)?;
|
||||||
}
|
}
|
||||||
|
@ -864,7 +869,7 @@ impl Tree {
|
||||||
let_or_var,
|
let_or_var,
|
||||||
name,
|
name,
|
||||||
explicit_type,
|
explicit_type,
|
||||||
assignment,
|
..
|
||||||
} => {
|
} => {
|
||||||
self.render_node(writer, *name, indent)?;
|
self.render_node(writer, *name, indent)?;
|
||||||
explicit_type.map(|ty| self.render_node(writer, ty, 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())?;
|
write!(writer, ", ty: {}", self.get_typename_str(*ty).unwrap())?;
|
||||||
}
|
}
|
||||||
writeln!(writer, ");")?;
|
writeln!(writer, ");")?;
|
||||||
if let Some(assignment) = assignment {
|
|
||||||
self.render_node(writer, *assignment, indent)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Tag::CallExpr { lhs, rhs } => todo!(),
|
Tag::CallExpr { lhs, rhs } => todo!(),
|
||||||
|
@ -1145,6 +1147,7 @@ impl Tree {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Tag::Assign { lhs, rhs } => {
|
Tag::Assign { lhs, rhs } => {
|
||||||
|
self.render_node(writer, *lhs, indent)?;
|
||||||
self.render_node(writer, *rhs, indent)?;
|
self.render_node(writer, *rhs, indent)?;
|
||||||
writeln_indented!(
|
writeln_indented!(
|
||||||
indent,
|
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