use crate::{ codegen::{expr::*, stmt::*, bool_to_i1, bool_to_i8, CodeGenContext}, symbol_resolver::ValueEnum, toplevel::{DefinitionId, TopLevelDef}, typecheck::typedef::{FunSignature, Type}, }; use inkwell::{ context::Context, types::{BasicTypeEnum, IntType}, values::{BasicValueEnum, IntValue, PointerValue}, }; use nac3parser::ast::{Expr, Stmt, StrRef}; pub trait CodeGenerator { /// Return the module name for the code generator. fn get_name(&self) -> &str; fn get_size_type<'ctx>(&self, ctx: &'ctx Context) -> IntType<'ctx>; /// Generate function call and returns the function return value. /// - obj: Optional object for method call. /// - fun: Function signature and definition ID. /// - params: Function parameters. Note that this does not include the object even if the /// function is a class method. fn gen_call<'ctx>( &mut self, ctx: &mut CodeGenContext<'ctx, '_>, obj: Option<(Type, ValueEnum<'ctx>)>, fun: (&FunSignature, DefinitionId), params: Vec<(Option, ValueEnum<'ctx>)>, ) -> Result>, String> where Self: Sized, { gen_call(self, ctx, obj, fun, params) } /// Generate object constructor and returns the constructed object. /// - signature: Function signature of the constructor. /// - def: Class definition for the constructor class. /// - params: Function parameters. fn gen_constructor<'ctx>( &mut self, ctx: &mut CodeGenContext<'ctx, '_>, signature: &FunSignature, def: &TopLevelDef, params: Vec<(Option, ValueEnum<'ctx>)>, ) -> Result, String> where Self: Sized, { gen_constructor(self, ctx, signature, def, params) } /// Generate a function instance. /// - obj: Optional object for method call. /// - fun: Function signature, definition ID and the substitution key. /// - params: Function parameters. Note that this does not include the object even if the /// function is a class method. /// Note that this function should check if the function is generated in another thread (due to /// possible race condition), see the default implementation for an example. fn gen_func_instance<'ctx>( &mut self, ctx: &mut CodeGenContext<'ctx, '_>, obj: Option<(Type, ValueEnum<'ctx>)>, fun: (&FunSignature, &mut TopLevelDef, String), id: usize, ) -> Result { gen_func_instance(ctx, &obj, fun, id) } /// Generate the code for an expression. fn gen_expr<'ctx>( &mut self, ctx: &mut CodeGenContext<'ctx, '_>, expr: &Expr>, ) -> Result>, String> where Self: Sized, { gen_expr(self, ctx, expr) } /// Allocate memory for a variable and return a pointer pointing to it. /// The default implementation places the allocations at the start of the function. fn gen_var_alloc<'ctx>( &mut self, ctx: &mut CodeGenContext<'ctx, '_>, ty: BasicTypeEnum<'ctx>, name: Option<&str>, ) -> Result, String> { gen_var(ctx, ty, name) } /// Return a pointer pointing to the target of the expression. fn gen_store_target<'ctx>( &mut self, ctx: &mut CodeGenContext<'ctx, '_>, pattern: &Expr>, name: Option<&str>, ) -> Result>, String> where Self: Sized, { gen_store_target(self, ctx, pattern, name) } /// Generate code for an assignment expression. fn gen_assign<'ctx>( &mut self, ctx: &mut CodeGenContext<'ctx, '_>, target: &Expr>, value: ValueEnum<'ctx>, ) -> Result<(), String> where Self: Sized, { gen_assign(self, ctx, target, value) } /// Generate code for a while expression. /// Return true if the while loop must early return fn gen_while( &mut self, ctx: &mut CodeGenContext<'_, '_>, stmt: &Stmt>, ) -> Result<(), String> where Self: Sized, { gen_while(self, ctx, stmt) } /// Generate code for a while expression. /// Return true if the while loop must early return fn gen_for( &mut self, ctx: &mut CodeGenContext<'_, '_>, stmt: &Stmt>, ) -> Result<(), String> where Self: Sized, { gen_for(self, ctx, stmt) } /// Generate code for an if expression. /// Return true if the statement must early return fn gen_if( &mut self, ctx: &mut CodeGenContext<'_, '_>, stmt: &Stmt>, ) -> Result<(), String> where Self: Sized, { gen_if(self, ctx, stmt) } fn gen_with( &mut self, ctx: &mut CodeGenContext<'_, '_>, stmt: &Stmt>, ) -> Result<(), String> where Self: Sized, { gen_with(self, ctx, stmt) } /// Generate code for a statement /// /// Return true if the statement must early return fn gen_stmt( &mut self, ctx: &mut CodeGenContext<'_, '_>, stmt: &Stmt>, ) -> Result<(), String> where Self: Sized, { gen_stmt(self, ctx, stmt) } /// Generates code for a block statement. fn gen_block<'a, I: Iterator>>>( &mut self, ctx: &mut CodeGenContext<'_, '_>, stmts: I, ) -> Result<(), String> where Self: Sized, { gen_block(self, ctx, stmts) } /// See [`bool_to_i1`]. fn bool_to_i1<'ctx>( &self, ctx: &CodeGenContext<'ctx, '_>, bool_value: IntValue<'ctx> ) -> IntValue<'ctx> { bool_to_i1(&ctx.builder, bool_value) } /// See [`bool_to_i8`]. fn bool_to_i8<'ctx>( &self, ctx: &CodeGenContext<'ctx, '_>, bool_value: IntValue<'ctx> ) -> IntValue<'ctx> { bool_to_i8(&ctx.builder, ctx.ctx, bool_value) } } pub struct DefaultCodeGenerator { name: String, size_t: u32, } impl DefaultCodeGenerator { #[must_use] pub fn new(name: String, size_t: u32) -> DefaultCodeGenerator { assert!(matches!(size_t, 32 | 64)); DefaultCodeGenerator { name, size_t } } } impl CodeGenerator for DefaultCodeGenerator { /// Returns the name for this [`CodeGenerator`]. fn get_name(&self) -> &str { &self.name } /// Returns an LLVM integer type representing `size_t`. fn get_size_type<'ctx>(&self, ctx: &'ctx Context) -> IntType<'ctx> { // it should be unsigned, but we don't really need unsigned and this could save us from // having to do a bit cast... if self.size_t == 32 { ctx.i32_type() } else { ctx.i64_type() } } }