From 6a59bf6d5345fbe2487e1cc36c36aa6884fcc39d Mon Sep 17 00:00:00 2001 From: Cody Date: Sun, 25 Jun 2023 14:35:24 -0500 Subject: brain damage --- examples/hello.sloth | 18 ++-- sloth/src/analysis/mod.rs | 8 +- sloth/src/analysis/setup.rs | 147 ++++++++++++++++++++++++++++++- sloth/src/codegen/mod.rs | 204 ++++++++++++++++++++++++++++++++++++++++++++ sloth/src/main.rs | 9 +- sloth/src/parser/ast.rs | 30 +++++-- 6 files changed, 395 insertions(+), 21 deletions(-) create mode 100644 sloth/src/codegen/mod.rs diff --git a/examples/hello.sloth b/examples/hello.sloth index a955e66..5deb0fc 100644 --- a/examples/hello.sloth +++ b/examples/hello.sloth @@ -1,11 +1,13 @@ -foreign fn example(arg: Int) Void; +# foreign fn example(); -fn main() { - var i: Int = 10; - var j: Int = 1.0 - 1; - while i > j { - i = i - 1; - } - example(i); +foreign fn foo() Int; +foreign fn bar(y: Int, z: Int) Float; + +fn main() Int { + foo(); + foo(); + foo(); + bar(5, 8 * 3 + foo()); + return 7 + 5; } diff --git a/sloth/src/analysis/mod.rs b/sloth/src/analysis/mod.rs index 8232e0e..29f1a91 100644 --- a/sloth/src/analysis/mod.rs +++ b/sloth/src/analysis/mod.rs @@ -8,8 +8,8 @@ pub enum AnalysisError { TypeMismatch(u32), #[error("Unknown identifier '{1}'")] UnknownIdentifier(u32, String), - #[error("Unknown error")] - Unknown(u32), + #[error("Unknown error '{1}'")] + Unknown(u32, &'static str), } impl AnalysisError { @@ -17,13 +17,15 @@ impl AnalysisError { match self { AnalysisError::TypeMismatch(line) => *line, AnalysisError::UnknownIdentifier(line, ..) => *line, - AnalysisError::Unknown(line) => *line, + AnalysisError::Unknown(line, ..) => *line, } } } pub fn analyze(root: &mut Stmt) -> Result<(), AnalysisError> { setup::populate_symtable(&root.as_node())?; + setup::propagate_types_stmt(root)?; + check_usage(&root.as_node())?; Ok(()) diff --git a/sloth/src/analysis/setup.rs b/sloth/src/analysis/setup.rs index 0c4f228..e690131 100644 --- a/sloth/src/analysis/setup.rs +++ b/sloth/src/analysis/setup.rs @@ -1,5 +1,7 @@ use super::AnalysisError; -use crate::parser::ast::{AstNode, Function, FunctionInput, FunctionKind, StmtKind}; +use crate::parser::ast::{ + AstNode, Expr, ExprKind, Function, FunctionInput, FunctionKind, Literal, Stmt, StmtKind, +}; use crate::symtable::{Symbol, SymbolTable, Type}; pub(super) fn populate_symtable(node: &AstNode) -> Result<(), AnalysisError> { @@ -75,3 +77,146 @@ fn build_function_symbol( output: output.into(), })) } + +pub(super) fn propagate_types_stmt(node: &mut Stmt) -> Result<(), AnalysisError> { + match &mut node.kind { + StmtKind::Block(children) => { + for child in children { + propagate_types_stmt(child)?; + } + } + StmtKind::ExprStmt(expr) => { + propagate_types(expr)?; + } + StmtKind::IfStmt { + condition, + if_then, + else_then, + } => { + propagate_types(condition)?; + propagate_types_stmt(if_then)?; + if let Some(else_then) = else_then { + propagate_types_stmt(else_then)?; + } + } + StmtKind::WhileStmt { condition, body } => { + propagate_types(condition)?; + propagate_types_stmt(body)?; + } + StmtKind::DefineVariable { value, .. } => { + propagate_types(value)?; + } + StmtKind::AssignVariable { value, .. } => { + propagate_types(value)?; + } + StmtKind::DefineFunction(function) => { + if let FunctionKind::Normal { body } = &mut function.kind { + propagate_types_stmt(body)?; + } + } + StmtKind::Return(expr) => { + propagate_types(expr)?; + } + } + + Ok(()) +} + +pub(super) fn propagate_types(node: &mut Expr) -> Result<(), AnalysisError> { + let typ = match &mut node.kind { + ExprKind::Grouping(child) => { + propagate_types(child)?; + child + .typ + .clone() + .ok_or(AnalysisError::Unknown(node.line, "owo choco"))? + } + ExprKind::Literal(lit) => match lit { + Literal::Integer(_) => Type::Integer, + Literal::Float(_) => Type::Float, + Literal::Boolean(_) => Type::Boolean, + _ => todo!(), + }, + ExprKind::Identifier(identifier) => { + let table = node.symtable.clone(); + table + .get_value(identifier) + .ok_or(AnalysisError::UnknownIdentifier( + node.line, + identifier.to_owned(), + ))? + } + ExprKind::BinaryOp { lhs, rhs, .. } => { + // Propagating the types to the children + propagate_types(lhs)?; + propagate_types(rhs)?; + + if lhs.typ != rhs.typ { + return Err(AnalysisError::TypeMismatch(node.line)); + } + + lhs.typ + .clone() + .ok_or(AnalysisError::Unknown(node.line, "owo?? choco???"))? + } + ExprKind::UnaryOp { value, .. } => { + propagate_types(value)?; + + value.typ.clone().ok_or(AnalysisError::Unknown( + node.line, + "YOU'RE WRONG... SULFURIC ACID!", + ))? + } + ExprKind::Call { callee, args } => { + propagate_types(callee)?; + for arg in args { + propagate_types(arg)?; + } + + let Some(Type::Function { ref output, .. }) = callee.typ else { + return Err(AnalysisError::TypeMismatch(node.line)); + }; + + *output.clone() + } + }; + + node.typ = Some(typ); + + Ok(()) +} + +#[cfg(test)] +mod tests { + use crate::analysis::setup::propagate_types; + use crate::parser::ast::{BinaryOp, Expr, ExprKind, Literal}; + use crate::symtable::{Symbol, SymbolTable, Type}; + + #[test] + fn haiiiiiuwu() { + let mut table = SymbolTable::new(); + table.insert("poggo".to_owned(), Symbol::Value(Type::Integer)); + table.insert("poggu".to_owned(), Symbol::Value(Type::Float)); + + let mut x = Expr::new( + 0, + 0, + ExprKind::BinaryOp { + op: BinaryOp::Add, + lhs: Box::new(Expr::new(1, 0, Literal::Float(1.).into(), table.clone())), + rhs: Box::new(Expr::new( + 2, + 0, + ExprKind::Identifier("poggu".to_owned()), + table.clone(), + )), + }, + table, + ); + + propagate_types(&mut x).expect("oh noes something went fucky wucky >~<"); + + println!("{x:#?}"); + panic!() + } +} diff --git a/sloth/src/codegen/mod.rs b/sloth/src/codegen/mod.rs new file mode 100644 index 0000000..fb83c99 --- /dev/null +++ b/sloth/src/codegen/mod.rs @@ -0,0 +1,204 @@ +use std::io::Write; + +use inkwell::builder::Builder; +use inkwell::context::Context; +use inkwell::module::Module; +use inkwell::types::{BasicMetadataTypeEnum, BasicType}; +use inkwell::values::{BasicMetadataValueEnum, BasicValue, BasicValueEnum, FunctionValue}; +use itertools::Itertools; + +use crate::parser::ast::{ + BinaryOp, Expr, ExprKind, Function, FunctionKind, Literal, Stmt, StmtKind, +}; +use crate::symtable::{SymbolTable, Type}; + +pub struct Compiler<'ctx> { + context: &'ctx Context, + builder: Builder<'ctx>, + module: Module<'ctx>, + + current_func: Option>, +} + +impl<'ctx> Compiler<'ctx> { + pub fn codegen(context: &'ctx Context, module: &str, code: &Stmt) { + let builder = context.create_builder(); + let module = context.create_module(module); + + let mut this = Compiler { + context, + builder, + module, + + current_func: None, + }; + + let StmtKind::Block(ref stmts) = &code.kind else { + panic!("Code root should be a block"); + }; + + for stmt in stmts { + this.codegen_stmt(stmt); + this.current_func.unwrap().print_to_stderr(); + } + } + + fn codegen_stmt(&mut self, code: &Stmt) { + match &code.kind { + StmtKind::Block(stmts) => self.codegen_block(stmts), + StmtKind::ExprStmt(expr) => { + self.codegen_expr(expr); + } + StmtKind::Return(expr) => { + let res = self.codegen_expr(expr); + self.builder.build_return(Some(&res)); + } + StmtKind::DefineFunction(function) => { + let table = code.symtable.clone(); + self.codegen_function(table, function.clone()); + + // If the function is written in sloth (as opposed to an extern one) we generate + // the block contents + if let FunctionKind::Normal { body } = &function.kind { + if let StmtKind::Block(body) = &body.kind { + self.codegen_block(body); + } + }; + } + _ => (), + } + } + + fn codegen_function(&mut self, table: SymbolTable, function: Function) -> FunctionValue { + let inputs = function.inputs; + let inputs_typ = inputs + .iter() + .map(|it| table.get_type(&it.typ).unwrap()) + .map(|it| self.type_as_metadata_type(it)) + .collect_vec(); + + let output = function.output; + let output_typ = output + .map(|it| table.get_type(&it)) + .unwrap_or(Some(Type::Void)) + .unwrap(); + + let llvm_function_type = match output_typ { + Type::Void => self.context.void_type().fn_type(&inputs_typ, false), + Type::Integer => self.context.i64_type().fn_type(&inputs_typ, false), + Type::Float => self.context.f64_type().fn_type(&inputs_typ, false), + Type::Boolean => self.context.bool_type().fn_type(&inputs_typ, false), + _ => todo!(), + }; + + let llvm_function = + self.module + .add_function(&function.identifier, llvm_function_type, None); + + self.current_func = Some(llvm_function); + + llvm_function + } + + fn codegen_block(&mut self, code: &[Stmt]) { + let Some(current_func) = self.current_func else { + panic!("Block codegen requires function"); + }; + + let block = self.context.append_basic_block(current_func, "block"); + + self.builder.position_at_end(block); + + for stmt in code { + self.codegen_stmt(stmt); + } + } + + fn codegen_expr(&self, code: &Expr) -> BasicValueEnum<'ctx> { + // AnyValue + match &code.kind { + ExprKind::Literal(literal) => self.codegen_value(literal.clone()), + ExprKind::Grouping(inner) => self.codegen_expr(inner), + ExprKind::Identifier(ident) => { + // FIXME: Do thsi + todo!() + } + ExprKind::BinaryOp { op, lhs, rhs } => match lhs.typ { + Some(Type::Integer) => { + let lhs_gen = self.codegen_expr(lhs).into_int_value(); + let rhs_gen = self.codegen_expr(rhs).into_int_value(); + + match op { + BinaryOp::Add => self.builder.build_int_add(lhs_gen, rhs_gen, "add"), + BinaryOp::Sub => self.builder.build_int_sub(lhs_gen, rhs_gen, "sub"), + BinaryOp::Mul => self.builder.build_int_mul(lhs_gen, rhs_gen, "mul"), + BinaryOp::Div => self.builder.build_int_signed_div(lhs_gen, rhs_gen, "div"), + _ => panic!(), + } + .into() + } + Some(Type::Float) => { + let lhs_gen = self.codegen_expr(lhs).into_float_value(); + let rhs_gen = self.codegen_expr(rhs).into_float_value(); + + match op { + BinaryOp::Add => self.builder.build_float_add(lhs_gen, rhs_gen, "add"), + BinaryOp::Sub => self.builder.build_float_sub(lhs_gen, rhs_gen, "sub"), + BinaryOp::Mul => self.builder.build_float_mul(lhs_gen, rhs_gen, "mul"), + BinaryOp::Div => self.builder.build_float_div(lhs_gen, rhs_gen, "div"), + _ => panic!(), + } + .into() + } + None => unreachable!("Critical Error: Type should never be null by this point"), + _ => todo!(), + }, + ExprKind::UnaryOp { op, value } => todo!(), + ExprKind::Call { callee, args } => { + // FIXME: Callee is an expression but for now were just + // extracting an identifier to it. Change this + // so you can do for example `fn(){}()`. + let ExprKind::Identifier(ident) = &callee.kind else { panic!() }; + let function = self.module.get_function(ident).expect("oh nooos"); + + let args = args + .iter() + .map(|arg| self.codegen_expr(arg)) + .map(|arg| arg.into()) + .collect::>(); + + self.builder + .build_call(function, &args, "") + .try_as_basic_value() + .unwrap_left() + } + } + } + + fn codegen_value(&self, value: Literal) -> BasicValueEnum<'ctx> { + match value { + Literal::Integer(value) => self + .context + .i64_type() + .const_int(value as u64, true) + .as_basic_value_enum(), + Literal::Float(value) => self + .context + .f64_type() + .const_float(value) + .as_basic_value_enum(), + _ => unimplemented!(), + } + } + + fn type_as_metadata_type(&self, typ: Type) -> BasicMetadataTypeEnum<'ctx> { + match typ { + Type::Integer => self.context.i64_type().into(), + Type::Float => self.context.f64_type().into(), + Type::Boolean => self.context.bool_type().into(), + _ => todo!(), // Type::Function { inputs, output } => todo!(), + } + } + + fn write_obj(&self, file: &mut impl Write) {} +} diff --git a/sloth/src/main.rs b/sloth/src/main.rs index ec9eb7e..bdcb4cb 100644 --- a/sloth/src/main.rs +++ b/sloth/src/main.rs @@ -8,6 +8,7 @@ )] pub mod analysis; +pub mod codegen; pub mod lexer; pub mod parser; pub mod sloth_std; @@ -15,6 +16,8 @@ pub mod symtable; use std::{env, fs}; +use codegen::Compiler; +use inkwell::context::Context; use itertools::Itertools; use lexer::Lexer; use parser::AstParser; @@ -55,9 +58,11 @@ fn main() { return; } - println!("Suces"); - // println!("{ast:#?}"); + // println!("Suces"); + + let context = Context::create(); + Compiler::codegen(&context, "hi", &ast); // let graph = GraphBuilder::generate(Some(&source), &ast).unwrap(); // println!("{graph}"); diff --git a/sloth/src/parser/ast.rs b/sloth/src/parser/ast.rs index 8b58fcd..281cd86 100644 --- a/sloth/src/parser/ast.rs +++ b/sloth/src/parser/ast.rs @@ -2,7 +2,7 @@ use std::fmt::Display; use crate::lexer::{self, TokenType}; use crate::parser::ParsingError; -use crate::symtable::SymbolTable; +use crate::symtable::{SymbolTable, Type}; #[derive(PartialEq, Clone, Debug)] /// AstNode that is either an Expr or Stmt, typically used for iterating over an @@ -36,6 +36,11 @@ pub struct Expr { pub line: u32, pub kind: ExprKind, pub symtable: SymbolTable, + + /// Type of the expression. If None it means the type hasn't yet been + /// checked. + pub typ: Option, + pub is_const: bool, } impl PartialEq for Expr { @@ -46,22 +51,33 @@ impl PartialEq for Expr { impl Expr { pub fn new(id: i32, line: u32, kind: ExprKind, symtable: SymbolTable) -> Self { + /// Recursivly check if a expression is constant + fn is_const(kind: &ExprKind) -> bool { + match kind { + ExprKind::Literal(_) => true, + ExprKind::Grouping(child) => is_const(&child.kind), + ExprKind::BinaryOp { lhs, rhs, .. } => is_const(&lhs.kind) && is_const(&rhs.kind), + ExprKind::UnaryOp { value, .. } => is_const(&value.kind), + ExprKind::Call { .. } | ExprKind::Identifier(_) => false, + } + } + + let is_const = is_const(&kind); + Self { id, line, kind, symtable, + + typ: None, + is_const, } } /// Useful for testing pub fn without_table(id: i32, kind: ExprKind) -> Self { - Self { - id, - line: 0, - kind, - symtable: SymbolTable::new(), - } + Self::new(id, 0, kind, SymbolTable::new()) } pub fn as_node(&self) -> AstNode { -- cgit v1.2.3