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, 'a>( &mut self, ctx: &mut CodeGenContext<'ctx, 'a>, 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, 'a>( &mut self, ctx: &mut CodeGenContext<'ctx, 'a>, 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, 'a>( &mut self, ctx: &mut CodeGenContext<'ctx, 'a>, 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, 'a>( &mut self, ctx: &mut CodeGenContext<'ctx, 'a>, 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, 'a>( &mut self, ctx: &mut CodeGenContext<'ctx, 'a>, 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, 'a>( &mut self, ctx: &mut CodeGenContext<'ctx, 'a>, 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, 'a>( &mut self, ctx: &mut CodeGenContext<'ctx, 'a>, 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<'ctx, 'a>( &mut self, ctx: &mut CodeGenContext<'ctx, 'a>, 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<'ctx, 'a>( &mut self, ctx: &mut CodeGenContext<'ctx, 'a>, 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<'ctx, 'a>( &mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt>, ) -> Result<(), String> where Self: Sized, { gen_if(self, ctx, stmt) } fn gen_with<'ctx, 'a>( &mut self, ctx: &mut CodeGenContext<'ctx, 'a>, 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<'ctx, 'a>( &mut self, ctx: &mut CodeGenContext<'ctx, 'a>, stmt: &Stmt>, ) -> Result<(), String> where Self: Sized, { gen_stmt(self, ctx, stmt) } /// Converts the value of [a boolean-like value][bool_value] into an `i1`. fn bool_to_i1<'ctx, 'a>( &self, ctx: &CodeGenContext<'ctx, 'a>, bool_value: IntValue<'ctx> ) -> IntValue<'ctx> { bool_to_i1(&ctx.builder, bool_value) } /// Converts the value of [a boolean-like value][bool_value] into an `i8`. fn bool_to_i8<'ctx, 'a>( &self, ctx: &CodeGenContext<'ctx, 'a>, 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 { pub fn new(name: String, size_t: u32) -> DefaultCodeGenerator { assert!(size_t == 32 || size_t == 64); DefaultCodeGenerator { name, size_t } } } impl CodeGenerator for DefaultCodeGenerator { fn get_name(&self) -> &str { &self.name } 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() } } }