From 9c6ff27ba4ce313887251f59f45a220556e94398 Mon Sep 17 00:00:00 2001 From: Cody Date: Fri, 9 Jun 2023 18:20:22 -0500 Subject: HeheheheHAahhahaha --- sloth/src/codegen.rs | 90 +++++++++++++++++++++++++ sloth/src/compiler.rs | 57 ++++++++++++++++ sloth/src/compiler/mod.rs | 131 ------------------------------------- sloth/src/compiler/mod.rs.disabled | 131 +++++++++++++++++++++++++++++++++++++ sloth/src/main.rs | 11 ++-- sloth/src/symbol.rs | 90 +++++++++++++++++++++++++ 6 files changed, 375 insertions(+), 135 deletions(-) create mode 100644 sloth/src/codegen.rs create mode 100644 sloth/src/compiler.rs delete mode 100644 sloth/src/compiler/mod.rs create mode 100644 sloth/src/compiler/mod.rs.disabled create mode 100644 sloth/src/symbol.rs diff --git a/sloth/src/codegen.rs b/sloth/src/codegen.rs new file mode 100644 index 0000000..217be71 --- /dev/null +++ b/sloth/src/codegen.rs @@ -0,0 +1,90 @@ +use inkwell::builder::Builder; +use inkwell::context::Context; +use inkwell::module::Module; +use inkwell::types::{AnyTypeEnum, BasicMetadataTypeEnum, BasicType, BasicTypeEnum, FunctionType}; +use inkwell::values::FunctionValue; +use itertools::Itertools; + +use crate::parser::ast::{FuncArgs, Stmt}; +use crate::symbol::SymbolTableStack; + +/// A module codegen is a struct designated to compiling a single module +pub struct ModuleCodegen<'ctx, 'a> { + context: &'ctx Context, + builder: Builder<'ctx>, + module: Module<'ctx>, + + symbol_table: &'a mut SymbolTableStack, +} + +impl<'ctx, 'a> ModuleCodegen<'ctx, 'a> { + pub fn new( + module: &str, + context: &'ctx Context, + symbol_table: &'a mut SymbolTableStack, + ) -> Self { + let builder = context.create_builder(); + let module = context.create_module(module); + + Self { + context, + builder, + module, + + symbol_table, + } + } + + pub fn compile_function( + &mut self, + ident: String, + args: Vec, + return_type: Option, + body: &[Stmt], + ) { + let llvm_function_type = self.compile_function_type(&args, return_type.as_deref()); + let llvm_function = self.module.add_function(&ident, llvm_function_type, None); + + let entry_block = self.context.append_basic_block(llvm_function, "entry"); + + self.block(body); + } + + fn compile_function_type( + &self, + args: &[FuncArgs], + return_type: Option<&str>, + ) -> FunctionType<'ctx> { + let args = args + .iter() + .map(|it| { + self.compile_basic_metadata_type(it.typ.as_ref().unwrap()) + .unwrap() + }) + .collect_vec(); + + match return_type { + None => self.context.void_type().fn_type(&args, false), + Some("int") => self.context.i64_type().fn_type(&args, false), + Some("float") => self.context.f64_type().fn_type(&args, false), + _ => panic!(), + } + } + + fn compile_basic_metadata_type(&self, typ: &str) -> Option> { + match typ { + "int" => Some(self.context.i64_type().into()), + "float" => Some(self.context.f64_type().into()), + _ => None, + } + } + + fn block(&mut self, body: &[Stmt]) { + self.symbol_table.push(); + self.symbol_table.pop(); + } + + // fn compile(symbol_table: &'a mut SymbolTableStack, code: Vec) { + // // + // } +} diff --git a/sloth/src/compiler.rs b/sloth/src/compiler.rs new file mode 100644 index 0000000..2a02ce9 --- /dev/null +++ b/sloth/src/compiler.rs @@ -0,0 +1,57 @@ +use inkwell::context::Context; +use thiserror::Error; + +use crate::codegen::{self, ModuleCodegen}; +use crate::parser::ast::Stmt; +use crate::symbol::{Symbol, SymbolTable, SymbolTableStack, SymbolType}; + +#[derive(Debug, Error)] +pub enum CompilerError { + #[error("Unknown compiler error")] + Unknown, +} + +pub struct Compiler { + symbol_table: SymbolTableStack, +} + +impl Compiler { + /// Take in a AST in the form of a vector of statements and compile the + /// program. + pub fn compile(code: Vec) -> Result<(), CompilerError> { + let mut compiler = Self { + symbol_table: SymbolTableStack::new(), + }; + + // Resolve names + compiler.resolve_globals(&code); + + // Compile each function + let context = Context::create(); + let codegen = ModuleCodegen::new("root", &context, &mut compiler.symbol_table); + + for stmt in code.iter() { + if let Stmt::DefineFunction { body, .. } = stmt { + // compiler.compile_function(body); + } + } + + Ok(()) + } + + fn resolve_globals(&mut self, code: &[Stmt]) { + for stmt in code.iter() { + if let Stmt::DefineFunction { ident, .. } = stmt { + let symbol = Symbol { + typ: Some(SymbolType::Function), + }; + + self.symbol_table.insert(ident, symbol); + } + } + } +} + +// Step 1: Name resolution +// Step 2: Type checking +// Step 3: Code generation diff --git a/sloth/src/compiler/mod.rs b/sloth/src/compiler/mod.rs deleted file mode 100644 index 87c0618..0000000 --- a/sloth/src/compiler/mod.rs +++ /dev/null @@ -1,131 +0,0 @@ -#![allow(unused)] - -use std::collections::HashMap; -use std::path::Path; -use std::vec; - -use inkwell::builder::Builder; -use inkwell::context::Context; -use inkwell::module::Module; -use inkwell::targets::{ - CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, -}; -use inkwell::values::IntValue; -use inkwell::OptimizationLevel; - -use crate::parser::ast::{BinaryOp, Expr, FuncArgs, Literal, Stmt, UnaryOp}; - -pub struct Compiler<'ctx> { - context: &'ctx Context, - builder: Builder<'ctx>, - module: Module<'ctx>, -} - -impl<'ctx> Compiler<'ctx> { - pub fn new(context: &'ctx Context) -> Self { - let builder = context.create_builder(); - let module = context.create_module("sloth"); - - Self { - context, - builder, - module, - } - } - - pub fn compile(&self, src: Vec) { - for stmt in src { - match stmt { - Stmt::DefineFunction { - ident, - args, - body, - return_type, - } => { - self.compile_function(&ident, &args, return_type.is_some(), body); - } - _ => panic!("You may only define a function top level"), - } - } - - Target::initialize_native(&InitializationConfig::default()).unwrap(); - - let triple = TargetMachine::get_default_triple(); - let target = Target::from_triple(&triple).unwrap(); - let machine = target - .create_target_machine( - &triple, - "x86-64", - "", - OptimizationLevel::None, - RelocMode::Default, - CodeModel::Default, - ) - .unwrap(); - - self.module.set_triple(&triple); - machine - .write_to_file(&self.module, FileType::Object, Path::new("output.o")) - .unwrap(); - } - - fn compile_function(&self, identifier: &str, args: &[FuncArgs], returns: bool, src: Vec) { - let void_type = self.context.void_type(); - let i64_type = self.context.i64_type(); - - let function_type = if returns { - i64_type.fn_type(&vec![i64_type.into(); args.len()], false) - } else { - void_type.fn_type(&vec![i64_type.into(); args.len()], false) - }; - let function = self.module.add_function(identifier, function_type, None); - - let basic_block = self.context.append_basic_block(function, "body"); - - self.builder.position_at_end(basic_block); - - let mut arg_values = HashMap::::new(); - for (i, arg) in args.iter().enumerate() { - arg_values.insert( - arg.name.clone(), - function.get_nth_param(i as u32).unwrap().into_int_value(), - ); - } - - for stmt in src { - match stmt { - Stmt::Return { value } => match value { - Expr::BinaryOp { op, lhs, rhs } => { - let lhs = match *lhs { - Expr::Variable(a) => arg_values[&a], - _ => unimplemented!(), - }; - - let rhs = match *rhs { - Expr::Variable(a) => arg_values[&a], - _ => unimplemented!(), - }; - - let res = match op { - BinaryOp::Add => self.builder.build_int_add(lhs, rhs, "addop"), - BinaryOp::Sub => self.builder.build_int_sub(lhs, rhs, "subop"), - _ => unimplemented!(), - }; - - self.builder.build_return(Some(&res)); - return; - } - Expr::Variable(name) => { - let var = arg_values[&name]; - self.builder.build_return(Some(&var)); - return; - } - _ => unimplemented!(), - }, - _ => unimplemented!(), - } - } - - self.builder.build_return(None); - } -} diff --git a/sloth/src/compiler/mod.rs.disabled b/sloth/src/compiler/mod.rs.disabled new file mode 100644 index 0000000..87c0618 --- /dev/null +++ b/sloth/src/compiler/mod.rs.disabled @@ -0,0 +1,131 @@ +#![allow(unused)] + +use std::collections::HashMap; +use std::path::Path; +use std::vec; + +use inkwell::builder::Builder; +use inkwell::context::Context; +use inkwell::module::Module; +use inkwell::targets::{ + CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, +}; +use inkwell::values::IntValue; +use inkwell::OptimizationLevel; + +use crate::parser::ast::{BinaryOp, Expr, FuncArgs, Literal, Stmt, UnaryOp}; + +pub struct Compiler<'ctx> { + context: &'ctx Context, + builder: Builder<'ctx>, + module: Module<'ctx>, +} + +impl<'ctx> Compiler<'ctx> { + pub fn new(context: &'ctx Context) -> Self { + let builder = context.create_builder(); + let module = context.create_module("sloth"); + + Self { + context, + builder, + module, + } + } + + pub fn compile(&self, src: Vec) { + for stmt in src { + match stmt { + Stmt::DefineFunction { + ident, + args, + body, + return_type, + } => { + self.compile_function(&ident, &args, return_type.is_some(), body); + } + _ => panic!("You may only define a function top level"), + } + } + + Target::initialize_native(&InitializationConfig::default()).unwrap(); + + let triple = TargetMachine::get_default_triple(); + let target = Target::from_triple(&triple).unwrap(); + let machine = target + .create_target_machine( + &triple, + "x86-64", + "", + OptimizationLevel::None, + RelocMode::Default, + CodeModel::Default, + ) + .unwrap(); + + self.module.set_triple(&triple); + machine + .write_to_file(&self.module, FileType::Object, Path::new("output.o")) + .unwrap(); + } + + fn compile_function(&self, identifier: &str, args: &[FuncArgs], returns: bool, src: Vec) { + let void_type = self.context.void_type(); + let i64_type = self.context.i64_type(); + + let function_type = if returns { + i64_type.fn_type(&vec![i64_type.into(); args.len()], false) + } else { + void_type.fn_type(&vec![i64_type.into(); args.len()], false) + }; + let function = self.module.add_function(identifier, function_type, None); + + let basic_block = self.context.append_basic_block(function, "body"); + + self.builder.position_at_end(basic_block); + + let mut arg_values = HashMap::::new(); + for (i, arg) in args.iter().enumerate() { + arg_values.insert( + arg.name.clone(), + function.get_nth_param(i as u32).unwrap().into_int_value(), + ); + } + + for stmt in src { + match stmt { + Stmt::Return { value } => match value { + Expr::BinaryOp { op, lhs, rhs } => { + let lhs = match *lhs { + Expr::Variable(a) => arg_values[&a], + _ => unimplemented!(), + }; + + let rhs = match *rhs { + Expr::Variable(a) => arg_values[&a], + _ => unimplemented!(), + }; + + let res = match op { + BinaryOp::Add => self.builder.build_int_add(lhs, rhs, "addop"), + BinaryOp::Sub => self.builder.build_int_sub(lhs, rhs, "subop"), + _ => unimplemented!(), + }; + + self.builder.build_return(Some(&res)); + return; + } + Expr::Variable(name) => { + let var = arg_values[&name]; + self.builder.build_return(Some(&var)); + return; + } + _ => unimplemented!(), + }, + _ => unimplemented!(), + } + } + + self.builder.build_return(None); + } +} diff --git a/sloth/src/main.rs b/sloth/src/main.rs index a611156..0e429b5 100644 --- a/sloth/src/main.rs +++ b/sloth/src/main.rs @@ -6,14 +6,15 @@ unused_lifetimes )] +pub mod codegen; pub mod compiler; pub mod lexer; pub mod parser; +pub mod symbol; use std::{env, fs}; use compiler::Compiler; -use inkwell::context::Context; use itertools::Itertools; use lexer::Lexer; use parser::AstParser; @@ -36,8 +37,10 @@ fn main() { let tokens = Lexer::new(&source).collect_vec(); let ast = AstParser::new(tokens).parse(); - let context = Context::create(); - let compiler = Compiler::new(&context); + Compiler::compile(ast).unwrap(); - compiler.compile(ast); + // let context = Context::create(); + // let compiler = Compiler::new(&context); + // + // compiler.compile(ast); } diff --git a/sloth/src/symbol.rs b/sloth/src/symbol.rs new file mode 100644 index 0000000..416b042 --- /dev/null +++ b/sloth/src/symbol.rs @@ -0,0 +1,90 @@ +use std::collections::HashMap; + +// TODO: Change name with some sort of path to make modules possible + +#[derive(Debug)] +pub struct SymbolTableStack { + tables: Vec, +} + +impl Default for SymbolTableStack { + fn default() -> Self { + Self { + tables: vec![SymbolTable::default()], + } + } +} + +impl SymbolTableStack { + pub fn new() -> Self { + Self::default() + } + + pub fn get(&self, name: &str) -> Option<&Symbol> { + for table in self.tables.iter().rev() { + if let Some(symbol) = table.get(name) { + return Some(symbol); + } + } + + None + } + + /// Returning true means a symbol was overriden + pub fn insert(&mut self, name: impl Into, symbol: Symbol) -> bool { + let head = self.tables.len() - 1; + self.tables[head].insert(name, symbol) + } + + pub fn push(&mut self) { + self.tables.push(SymbolTable::default()); + } + + pub fn pop(&mut self) -> bool { + if self.tables.len() <= 1 { + // Symbol table stacks must always have atleast 1 stack + return false; + } + + self.tables.pop(); + true + } + + pub fn root(&self) -> &SymbolTable { + &self.tables[0] + } + + pub fn root_mut(&mut self) -> &mut SymbolTable { + &mut self.tables[0] + } +} + +#[derive(Debug, Default)] +pub struct SymbolTable { + symbols: HashMap, +} + +impl SymbolTable { + pub fn new() -> Self { + Self::default() + } + + pub fn get(&self, name: &str) -> Option<&Symbol> { + self.symbols.get(name) + } + + /// Returning true means a symbol was overriden + pub fn insert(&mut self, name: impl Into, symbol: Symbol) -> bool { + self.symbols.insert(name.into(), symbol).is_some() + } +} + +#[derive(Debug)] +pub struct Symbol { + pub typ: Option, +} + +#[derive(Debug)] +pub enum SymbolType { + Function, +} -- cgit v1.2.3