diff --git a/src/emitter.rs b/src/emitter.rs index 259c746..11e4e5a 100644 --- a/src/emitter.rs +++ b/src/emitter.rs @@ -3,12 +3,12 @@ use cranelift::prelude::*; use cranelift_codegen::{Context, isa, settings}; use cranelift_frontend::FunctionBuilderContext; -use cranelift_module::{Linkage, Module, default_libcall_names}; +use cranelift_module::{DataDescription, Linkage, Module, default_libcall_names}; use cranelift_object::{ObjectBuilder, ObjectModule}; use target_lexicon::HOST; use crate::parser::{ - ast::{Program, Statement}, + ast::{PrintItem, Program, Statement}, expression::{BinaryOperator, Expression, LiteralExpression}, }; @@ -31,15 +31,18 @@ fn main_signature(isa: &dyn isa::TargetIsa) -> Signature { } /// The emitter state. -#[allow(dead_code)] pub struct Emitter { module: ObjectModule, - function_context: FunctionBuilderContext, - context: Context, + data_count: usize, } -#[allow(unused_variables, dead_code)] impl Emitter { + /// Constructs a new instance of the emitter. + /// + /// This will error if any of the following are not met: + /// - Unidentified ISA + /// - We used the `target_lexicon` crate for ISA identification, so if its not supported by that it will fail. + /// - Invalid flags. If the user has passed flags for features not supported by an architecture it will fail. pub fn new() -> anyhow::Result { let shared_flags = settings::Flags::new(settings::builder()); let isa_builder = isa::lookup(HOST)?; @@ -48,24 +51,32 @@ impl Emitter { Ok(Self { module: ObjectModule::new(builder), - function_context: FunctionBuilderContext::new(), - context: Context::new(), + data_count: 0, }) } + /// Emits a single program to object code. + /// + /// This will consume the Emitter in order to produce the associated code. This means that a single emitter can only ever be used for one "program" (or file). pub fn emit_program(mut self, program: &Program) -> anyhow::Result> { let main_func_id = { let sig = main_signature(self.module.isa()); // Add this function to our Module. self.module - .declare_function("_start", Linkage::Export, &sig)? + .declare_function("main", Linkage::Export, &sig)? }; + + // much of the code here was sources from the examples here: https://github.com/simvux/cranelift-examples/blob/master/examples/ + // These contain the context needed for generating code for a function. // // It's a lot more efficient to construct them once, and then re-use them for all functions. + let mut context = Context::new(); + let mut function_context = FunctionBuilderContext::new(); - let mut builder = FunctionBuilder::new(&mut self.context.func, &mut self.function_context); + let mut builder = FunctionBuilder::new(&mut context.func, &mut function_context); + // always starts the basic program with a main function that calls each line sequentially. builder.func.signature = main_signature(self.module.isa()); // Create the functions entry block. @@ -78,24 +89,21 @@ impl Emitter { for line in &program.lines { for statement in &line.statements { - Self::emit_statement(&mut builder, statement); + self.emit_statement(&mut builder, statement)?; } } // returns 0 as a good return value for our program + // we should always return 0 at the end, unless some other return value is emmited previously let zero = builder.ins().iconst(types::I32, 0); builder.ins().return_(&[zero]); + // validates and finalizes the function generation if let Err(err) = codegen::verify_function(builder.func, self.module.isa()) { panic!("verifier error: {err}"); } - builder.finalize(); - - self.module - .define_function(main_func_id, &mut self.context)?; - - self.context.clear(); + self.module.define_function(main_func_id, &mut context)?; // Finalize the module to generate our `Product`. // @@ -108,26 +116,157 @@ impl Emitter { Ok(bytes) } - fn emit_statement(builder: &mut FunctionBuilder, stmt: &Statement) { + fn emit_statement( + &mut self, + builder: &mut FunctionBuilder, + stmt: &Statement, + ) -> anyhow::Result<()> { match stmt { // emits return logic if we hit something like this Statement::End | Statement::Return => { let zero = builder.ins().iconst(types::I32, 0); builder.ins().return_(&[zero]); + Ok(()) + } + Statement::Print { items } => { + self.emit_print_statement(builder, items)?; + Ok(()) } + Statement::Rem(_) => Ok(()), Statement::Expression(expr) => { - Self::emit_expression_inner(builder, expr); + self.emit_expression_inner(builder, expr)?; + Ok(()) } _ => todo!(), } } - fn emit_expression_inner(builder: &mut FunctionBuilder, expr: &Expression) -> Value { - match expr { - Expression::Literal(literal) => Self::emit_literal(builder, literal), + /// Emits a print statement by calling the printf function in the c std lib + /// + /// TODO: Tweak this such that it uses a slightly better call with features like variable expansion and such. + /// Its fine for right now for just dumping shit into stdout. I don't really like the way we call printf differently. + /// I think we can simplify this. + fn emit_print_statement( + &mut self, + builder: &mut FunctionBuilder, + items: &[PrintItem], + ) -> anyhow::Result<()> { + if items.is_empty() { + let value = self.emit_expression_inner(builder, &Expression::char('\n'))?; + self.emit_printf_i32_call(builder, value)?; + return Ok(()); + } + + for item in items { + match item { + PrintItem::String(value) => { + let value = self.emit_expression_inner( + builder, + &Expression::string(value.trim_matches('"').to_string()), + )?; + self.emit_printf_call(builder, value)?; + } + PrintItem::Char(value) => { + let value = Expression::char(*value); + let value = self.emit_expression_inner(builder, &value)?; + self.emit_printf_i32_call(builder, value)?; + } + PrintItem::Expression(value) => { + let value = self.emit_expression_inner(builder, value)?; + self.emit_printf_i32_call(builder, value)?; + } + } + } + + Ok(()) + } + + /// Emits a printf call which knows that it's calling on a single string + fn emit_printf_call( + &mut self, + builder: &mut FunctionBuilder, + value: Value, + ) -> anyhow::Result<()> { + // establishes the signature for printf which we will be calling + let ptr_type = self.module.target_config().pointer_type(); + let mut signature = Signature::new(self.module.isa().default_call_conv()); + signature.params.push(AbiParam::new(ptr_type)); + signature.returns.push(AbiParam::new(types::I32)); + let printf_id = self + .module + .declare_function("printf", Linkage::Import, &signature)?; + let printf = self.module.declare_func_in_func(printf_id, builder.func); + + // sets the format param if we're passing in a number + // TODO: Change this to support other types like float and such + + builder.ins().call(printf, &[value]); + Ok(()) + } + + /// Emits a printf call which knows its trying to output an integer + /// + /// TODO: I really hate how this is done. I want there to be a single printf call function, and not have two that have repeated code. + /// I also don't want to do too much work higher up when emitting code. I know I can't have it both ways, so for now we have two functions that + /// are almost identical but do slightly different work. + fn emit_printf_i32_call( + &mut self, + builder: &mut FunctionBuilder, + value: Value, + ) -> anyhow::Result<()> { + // establishes the signature for printf which we will be calling + let ptr_type = self.module.target_config().pointer_type(); + let mut signature = Signature::new(self.module.isa().default_call_conv()); + signature.params.push(AbiParam::new(ptr_type)); + signature.params.push(AbiParam::new(types::I32)); + + signature.returns.push(AbiParam::new(types::I32)); + let printf_id = self + .module + .declare_function("printf", Linkage::Import, &signature)?; + let printf = self.module.declare_func_in_func(printf_id, builder.func); + + // sets the format param if we're passing in a number + // TODO: Change this to support other types like float and such + let format = self.emit_string_pointer(builder, "%d\n")?; + + builder.ins().call(printf, &[format, value]); + Ok(()) + } + + fn emit_string_pointer( + &mut self, + builder: &mut FunctionBuilder, + value: &str, + ) -> anyhow::Result { + let name = format!("string_{}", self.data_count); + self.data_count += 1; + + let data_id = self + .module + .declare_data(&name, Linkage::Local, false, false)?; + let mut bytes = value.as_bytes().to_vec(); + bytes.push(0); + + let mut data = DataDescription::new(); + data.define(bytes.into_boxed_slice()); + self.module.define_data(data_id, &data)?; + + let data = self.module.declare_data_in_func(data_id, builder.func); + let ptr_type = self.module.target_config().pointer_type(); + Ok(builder.ins().global_value(ptr_type, data)) + } + + fn emit_expression_inner( + &mut self, + builder: &mut FunctionBuilder, + expr: &Expression, + ) -> anyhow::Result { + let value = match expr { + Expression::Literal(literal) => self.emit_literal(builder, literal)?, Expression::Binary(binary) => { - let left = Self::emit_expression_inner(builder, binary.left()); - let right = Self::emit_expression_inner(builder, binary.right()); + let left = self.emit_expression_inner(builder, binary.left())?; + let right = self.emit_expression_inner(builder, binary.right())?; match binary.operator() { BinaryOperator::Plus => builder.ins().iadd(left, right), @@ -153,7 +292,7 @@ impl Emitter { } } Expression::Grouping(grouping) => { - Self::emit_expression_inner(builder, grouping.expression()) + self.emit_expression_inner(builder, grouping.expression())? } Expression::Identifier(identifier) => { panic!( @@ -161,19 +300,30 @@ impl Emitter { identifier.name() ) } - } + }; + + Ok(value) } - fn emit_literal(builder: &mut FunctionBuilder, literal: &LiteralExpression) -> Value { - match literal { + fn emit_literal( + &mut self, + builder: &mut FunctionBuilder, + literal: &LiteralExpression, + ) -> anyhow::Result { + let value = match literal { LiteralExpression::Integer(value) => builder.ins().iconst(types::I32, *value), - LiteralExpression::Char(value) => { - builder.ins().iconst(types::I32, i64::from(*value as u32)) + LiteralExpression::Char(value) => builder + .ins() + .iconst(types::I32, i64::from(u32::from(*value))), + LiteralExpression::String(value) => { + self.emit_string_pointer(builder, &format!("{value}\n"))? } - LiteralExpression::Float(_) | LiteralExpression::String(_) => { - panic!("only integer and char literals are supported by the emitter") + LiteralExpression::Float(_) => { + panic!("only integer, char, and string literals are supported by the emitter") } - } + }; + + Ok(value) } } @@ -184,7 +334,10 @@ mod tests { use crate::{ emitter::{Emitter, main_signature}, - parser::expression::{BinaryOperator, Expression}, + parser::{ + ast::{Line, PrintItem, Program, Statement}, + expression::{BinaryOperator, Expression}, + }, }; fn emit_expression_ir(expr: &Expression) -> anyhow::Result { @@ -197,7 +350,8 @@ mod tests { builder.switch_to_block(block); builder.seal_block(block); - let value = Emitter::emit_expression_inner(&mut builder, expr); + let mut emitter = Emitter::new()?; + let value = emitter.emit_expression_inner(&mut builder, expr)?; builder.ins().return_(&[value]); builder.finalize(); @@ -223,6 +377,24 @@ mod tests { Ok(()) } + #[test] + fn emit_string_expression_program() -> anyhow::Result<()> { + let program = Program { + lines: vec![Line { + number: None, + statements: vec![Statement::Expression(Expression::string( + "\"HELLO\"".to_string(), + ))], + }], + }; + + let bytes = Emitter::new()?.emit_program(&program)?; + + assert!(!bytes.is_empty()); + + Ok(()) + } + #[test] fn emit_add_expression() -> anyhow::Result<()> { let ir = emit_expression_ir(&Expression::binary( @@ -257,4 +429,44 @@ mod tests { Ok(()) } + + #[test] + fn emit_print_string_program() -> anyhow::Result<()> { + let program = Program { + lines: vec![Line { + number: None, + statements: vec![Statement::Print { + items: vec![PrintItem::String("\"HELLO\"".to_string())], + }], + }], + }; + + let bytes = Emitter::new()?.emit_program(&program)?; + + assert!(!bytes.is_empty()); + + Ok(()) + } + + #[test] + fn emit_print_expression_program() -> anyhow::Result<()> { + let program = Program { + lines: vec![Line { + number: None, + statements: vec![Statement::Print { + items: vec![PrintItem::Expression(Expression::binary( + Expression::integer(1), + BinaryOperator::Plus, + Expression::integer(2), + ))], + }], + }], + }; + + let bytes = Emitter::new()?.emit_program(&program)?; + + assert!(!bytes.is_empty()); + + Ok(()) + } } diff --git a/src/parser.rs b/src/parser.rs index 7eda6f6..d6d221b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -35,11 +35,9 @@ impl<'a> Parser<'a> { } pub fn parse_line(&mut self) -> Line { - let line_num = self.lexer.peek().try_number(); - let statements = self.parse_statement_list(); Line { - number: line_num, + number: None, statements, } } @@ -91,13 +89,20 @@ impl<'a> Parser<'a> { self.lexer.peek(), Token::Colon | Token::Eof | Token::Semicolon ) { - let item = match self.lexer.next_token() { - Token::String(value) => PrintItem::String(value), - Token::Char(value) => PrintItem::Char(value), - Token::Integer(value) => PrintItem::Expression(Expression::integer(value)), - Token::Float(value) => PrintItem::Expression(Expression::float(value)), - Token::Identifier(value) => PrintItem::Expression(Expression::identifier(value)), - _ => break, + let item = match self.lexer.peek() { + Token::String(_) => match self.lexer.next_token() { + Token::String(value) => PrintItem::String(value), + _ => unreachable!(), + }, + Token::Char(_) => match self.lexer.next_token() { + Token::Char(value) => PrintItem::Char(value), + _ => unreachable!(), + }, + Token::Comma => { + self.lexer.next_token(); + continue; + } + _ => PrintItem::Expression(self.parse_expression()), }; items.push(item); @@ -332,6 +337,20 @@ mod tests { ); } + #[test] + fn parse_print_expression_statement_list() { + assert_eq!( + parse_statement_list("print 1 + 2;"), + vec![Statement::Print { + items: vec![PrintItem::Expression(Expression::binary( + Expression::integer(1), + BinaryOperator::Plus, + Expression::integer(2), + ))] + }] + ); + } + #[test] fn parse_print_identifier_statement_list() { assert_eq!( @@ -499,7 +518,7 @@ mod tests { parser.parse_program(), Program { lines: vec![Line { - number: Some(1), + number: None, statements: vec![Statement::Expression(Expression::binary( Expression::integer(1), BinaryOperator::Plus,