diff --git a/.gitignore b/.gitignore index 4531364..b37eab4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target -VERSIONING/ \ No newline at end of file +VERSIONING/ +*.ll \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index b4a0dc5..a28c954 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,160 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "cc" +version = "1.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362" +dependencies = [ + "shlex", +] + [[package]] name = "delta" version = "1.0.0" +dependencies = [ + "inkwell", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "inkwell" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b597a7b2cdf279aeef6d7149071e35e4bc87c2cf05a5b7f2d731300bffe587ea" +dependencies = [ + "either", + "inkwell_internals", + "libc", + "llvm-sys", + "once_cell", + "thiserror", +] + +[[package]] +name = "inkwell_internals" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fa4d8d74483041a882adaa9a29f633253a66dde85055f0495c121620ac484b2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "llvm-sys" +version = "170.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1ee92492ae76bb6e24495ce15e311ba13ffb58c0140d89ac9910b92477cec8" +dependencies = [ + "anyhow", + "cc", + "lazy_static", + "libc", + "regex-lite", + "semver", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" diff --git a/Cargo.toml b/Cargo.toml index b1077bc..9427a5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,5 +2,14 @@ name = "delta" version = "1.0.0" edition = "2024" +description = "Delta - A natural language inspired programming language" +authors = ["Pranav Verma "] + +[[bin]] +name = "delta" +path = "src/main.rs" [dependencies] +inkwell = { version = "0.4", features = ["llvm17-0"] } +# Future dependencies for LLVM backend +# llvm-sys = "170" diff --git a/examples/compile_test.de b/examples/compile_test.de new file mode 100644 index 0000000..9d22052 --- /dev/null +++ b/examples/compile_test.de @@ -0,0 +1,4 @@ +let x be 10 +let y be 5 +let result be x + y * 2 +show result \ No newline at end of file diff --git a/examples/hello_compiled.de b/examples/hello_compiled.de new file mode 100644 index 0000000..2ec302a --- /dev/null +++ b/examples/hello_compiled.de @@ -0,0 +1,3 @@ +show "Hello, compiled Delta language!" +let message be "This is a message from the compiled code." +show message \ No newline at end of file diff --git a/src/codegen.rs b/src/codegen.rs index a613bea..dc203b5 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -1,51 +1,291 @@ use crate::ast::*; use std::collections::HashMap; +use inkwell::context::Context; +use inkwell::builder::Builder; +use inkwell::module::Module; +use inkwell::values::{FloatValue, FunctionValue, PointerValue}; +use inkwell::types::FloatType; +use inkwell::FloatPredicate; +use std::error::Error; +use std::fmt; -pub struct CodeGenerator { - variables: HashMap, +#[derive(Debug)] +pub struct CodegenError { + message: String, } -impl CodeGenerator { - pub fn new() -> Self { - CodeGenerator { +impl fmt::Display for CodegenError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.message) + } +} + +impl Error for CodegenError {} + +pub struct CodeGenerator<'ctx> { + context: &'ctx Context, + module: Module<'ctx>, + builder: Builder<'ctx>, + variables: HashMap>, +} + +impl<'ctx> CodeGenerator<'ctx> { + pub fn new(context: &'ctx Context, module_name: &str) -> Result> { + let module = context.create_module(module_name); + let builder = context.create_builder(); + + Ok(CodeGenerator { + context, + module, + builder, variables: HashMap::new(), + }) + } + + fn get_float_type(&self) -> FloatType<'ctx> { + self.context.f64_type() + } + + pub fn compile(&mut self, program: &Program) -> Result<(), Box> { + let main_type = self.context.i32_type().fn_type(&[], false); + let main_fn = self.module.add_function("main", main_type, None); + let basic_block = self.context.append_basic_block(main_fn, "entry"); + self.builder.position_at_end(basic_block); + + self.add_printf_declaration(); + + for statement in &program.statements { + self.compile_statement(statement)?; + } + + let return_value = self.context.i32_type().const_int(0, false); + self.builder.build_return(Some(&return_value))?; + + if let Err(errors) = self.module.verify() { + return Err(Box::new(CodegenError { + message: format!("Module verification failed: {}", errors), + })); } + + println!("LLVM IR generated successfully!"); + self.module.print_to_stderr(); + Ok(()) + } + + fn add_printf_declaration(&mut self) { + let i8_type = self.context.i8_type(); + let i8_ptr_type = i8_type.ptr_type(inkwell::AddressSpace::default()); + let printf_type = self.context.i32_type().fn_type(&[i8_ptr_type.into()], true); + + self.module.add_function("printf", printf_type, None); } - - pub fn generate(&mut self, _program: &Program) -> Result<(), String> { - // TODO: Add LLVM IR Generation Code. - // For now, we wil just interpret the AST. - println!("Code generation not yet implemented - use interpreter"); + + fn compile_statement(&mut self, statement: &Statement) -> Result<(), Box> { + match statement { + Statement::Let(let_stmt) => { + let value = self.compile_expression(&let_stmt.value)?; + + let alloc_s = self.builder.build_alloca(self.get_float_type(), &let_stmt.identifier)?; + self.builder.build_store(alloc_s, value); + self.variables.insert(let_stmt.identifier.clone(), alloc_s); + } + Statement::Show(show_stmt) => { + let value = self.compile_expression(&show_stmt.value)?; + self.generate_print_call(value)?; + } + Statement::When(when_stmt) => { + self.compile_when_statement(when_stmt)?; + } + Statement::FunctionDef(_) => { + // TODO: Implement function definitions + println!("Function definitions not yet implemented in Compiler"); + } + Statement::Expression(expr) => { + self.compile_expression(expr)?; + } + } + Ok(()) } - + + // TODO: TO ADD ALL FUNCTIONS IN THE ABOVE FUNCTION. + + fn compile_when_statement(&mut self, when_stmt: &WhenStatement) -> Result<(), Box> { + let condition = self.compile_expression(&when_stmt.condition)?; + + let current_fn = self.builder.get_insert_block().unwrap().get_parent().unwrap(); + let then_block = self.context.append_basic_block(current_fn, "then"); + let else_block = self.context.append_basic_block(current_fn, "else"); + let merge_block = self.context.append_basic_block(current_fn, "merge"); + + // convert it to a bool (assume float) + let zero = self.get_float_type().const_float(0.0); + let cond_bool = self.builder.build_float_compare( + FloatPredicate::ONE, // not equal to zero (true mf) + condition, + zero, + "cond" + )?; + + self.builder.build_conditional_branch(cond_bool, then_block, else_block)?; + + // then block compilation + self.builder.position_at_end(then_block); + for stmt in &when_stmt.then_block { + self.compile_statement(stmt)?; + } + self.builder.build_unconditional_branch(merge_block)?; + + // else block compilation + self.builder.position_at_end(else_block); + if let Some(otherwise_block) = &when_stmt.otherwise_block { + for stmt in otherwise_block { + self.compile_statement(stmt)?; + } + } + self.builder.build_unconditional_branch(merge_block)?; + + // continue + self.builder.position_at_end(merge_block); + + Ok(()) + } + + fn compile_expression(&mut self, expr: &Expression) -> Result, Box> { + match expr { + Expression::Number(n) => { + Ok(self.get_float_type().const_float(*n)) + } + Expression::String(s) => { + // TODO: we need special handling for output, for now print it directly. + + let printf_fn = self.module.get_function("printf").unwrap(); + let string_ptr = self.builder.build_global_string_ptr(&format!("{}\n", s), "str")?; + + self.builder.build_call( + printf_fn, + &[string_ptr.as_pointer_value().into()], + "printf_string" + )?; + + // return 0 as the numeric value + Ok(self.get_float_type().const_float(0.0)) + } + Expression::Identifier(name) => { + if let Some(alloca) = self.variables.get(name) { + let loaded = self.builder.build_load(self.get_float_type(), *alloca, name)?; + Ok(loaded.into_float_value()) + } else { + Err(Box::new(CodegenError { + message: format!("Undefined variable: {}", name), + })) + } + } + Expression::BinaryOp(binop) => { + let left = self.compile_expression(&binop.left)?; + let right = self.compile_expression(&binop.right)?; + + match binop.operator { // This Match Statement is generated by AI (I aint writing this shit by hand) + BinaryOperator::Add => { + Ok(self.builder.build_float_add(left, right, "add")?) + } + BinaryOperator::Subtract => { + Ok(self.builder.build_float_sub(left, right, "sub")?) + } + BinaryOperator::Multiply => { + Ok(self.builder.build_float_mul(left, right, "mul")?) + } + BinaryOperator::Divide => { + Ok(self.builder.build_float_div(left, right, "div")?) + } + BinaryOperator::GreaterThan => { + let cmp = self.builder.build_float_compare(FloatPredicate::OGT, left, right, "gt")?; + let result = self.builder.build_unsigned_int_to_float(cmp, self.get_float_type(), "gt_float")?; + Ok(result) + } + BinaryOperator::LessThan => { + let cmp = self.builder.build_float_compare(FloatPredicate::OLT, left, right, "lt")?; + let result = self.builder.build_unsigned_int_to_float(cmp, self.get_float_type(), "lt_float")?; + Ok(result) + } + BinaryOperator::GreaterThanOrEqual => { + let cmp = self.builder.build_float_compare(FloatPredicate::OGE, left, right, "gte")?; + let result = self.builder.build_unsigned_int_to_float(cmp, self.get_float_type(), "gte_float")?; + Ok(result) + } + BinaryOperator::LessThanOrEqual => { + let cmp = self.builder.build_float_compare(FloatPredicate::OLE, left, right, "lte")?; + let result = self.builder.build_unsigned_int_to_float(cmp, self.get_float_type(), "lte_float")?; + Ok(result) + } + BinaryOperator::Equal => { + let cmp = self.builder.build_float_compare(FloatPredicate::OEQ, left, right, "eq")?; + let result = self.builder.build_unsigned_int_to_float(cmp, self.get_float_type(), "eq_float")?; + Ok(result) + } + BinaryOperator::NotEqual => { + let cmp = self.builder.build_float_compare(FloatPredicate::ONE, left, right, "ne")?; + let result = self.builder.build_unsigned_int_to_float(cmp, self.get_float_type(), "ne_float")?; + Ok(result) + } + } + } + Expression::FunctionCall(_) => { + // TODO: Implement function calls + Ok(self.get_float_type().const_float(0.0)) + } + } + } + + fn generate_print_call(&mut self, value: FloatValue<'ctx>) -> Result<(), Box> { + let printf_fn = self.module.get_function("printf").unwrap(); + + // create format str for print floatrs + let format_str = self.builder.build_global_string_ptr("%.2f\n", "fmt")?; + + self.builder.build_call( + printf_fn, + &[format_str.as_pointer_value().into(), value.into()], + "printf_call" + )?; + + Ok(()) + } + + pub fn save_to_file(&self, filename: &str) -> Result<(), Box> { + self.module.print_to_file(filename)?; + Ok(()) + } + + // keeping the int. for comp. pub fn interpret(&mut self, program: &Program) -> Result<(), String> { + let mut interpreter_vars: HashMap = HashMap::new(); + for statement in &program.statements { - self.interpret_statement(statement)?; + self.interpret_statement(statement, &mut interpreter_vars)?; } Ok(()) } - - fn interpret_statement(&mut self, statement: &Statement) -> Result<(), String> { + + fn interpret_statement(&self, statement: &Statement, variables: &mut HashMap) -> Result<(), String> { match statement { Statement::Show(show) => { - let value = self.evaluate_expression(&show.value)?; + let value = self.evaluate_expression(&show.value, variables)?; println!("{}", value); } Statement::Let(let_stmt) => { - let value = self.evaluate_expression(&let_stmt.value)?; - self.variables.insert(let_stmt.identifier.clone(), value); + let value = self.evaluate_expression(&let_stmt.value, variables)?; + variables.insert(let_stmt.identifier.clone(), value); } Statement::When(when_stmt) => { - let condition_result = self.evaluate_expression(&when_stmt.condition)?; - // For now, simple string-based evaluation + let condition_result = self.evaluate_expression(&when_stmt.condition, variables)?; if condition_result.contains("true") || condition_result.contains("True") { for stmt in &when_stmt.then_block { - self.interpret_statement(stmt)?; + self.interpret_statement(stmt, variables)?; } } else if let Some(otherwise_block) = &when_stmt.otherwise_block { for stmt in otherwise_block { - self.interpret_statement(stmt)?; + self.interpret_statement(stmt, variables)?; } } } @@ -53,29 +293,27 @@ impl CodeGenerator { println!("Defined function: {}", func_def.name); } Statement::Expression(expr) => { - let _value = self.evaluate_expression(expr)?; - // Expression statements don't print by default + let _value = self.evaluate_expression(expr, variables)?; } } Ok(()) } - - fn evaluate_expression(&mut self, expression: &Expression) -> Result { + + fn evaluate_expression(&self, expression: &Expression, variables: &HashMap) -> Result { match expression { Expression::Number(n) => Ok(n.to_string()), Expression::String(s) => Ok(s.clone()), Expression::Identifier(name) => { - if let Some(value) = self.variables.get(name) { + if let Some(value) = variables.get(name) { Ok(value.clone()) } else { Ok(format!("", name)) } } Expression::BinaryOp(binop) => { - let left_val = self.evaluate_expression(&binop.left)?; - let right_val = self.evaluate_expression(&binop.right)?; + let left_val = self.evaluate_expression(&binop.left, variables)?; + let right_val = self.evaluate_expression(&binop.right, variables)?; - // Try to parse as numbers for comparison and arithmetic if let (Ok(left_num), Ok(right_num)) = (left_val.parse::(), right_val.parse::()) { let result = match binop.operator { BinaryOperator::GreaterThan => return Ok((left_num > right_num).to_string()), diff --git a/src/main.rs b/src/main.rs index fed1028..eb9cb52 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,16 +10,26 @@ mod codegen; use lexer::Lexer; use parser::Parser; use codegen::CodeGenerator; +use inkwell::context::Context; fn main() { let args: Vec = env::args().collect(); - if args.len() != 2 { - eprintln!("Usage: {} ", args[0]); + if args.len() < 2 || args.len() > 3 { + eprintln!("Usage: {} [--compile|--interpret]", args[0]); + eprintln!(" --compile : Generate LLVM IR and compile (default)"); + eprintln!(" --interpret : Run in interpreter mode"); process::exit(1); } let filename = &args[1]; + + let mode = if args.len() == 3 { + &args[2] + } else { + "--compile" + }; + let source = match fs::read_to_string(filename) { Ok(content) => content, Err(err) => { @@ -28,7 +38,7 @@ fn main() { } }; - // Step 1: Tokenize (Lexer) + // Step 1: Tokenize let mut lexer = Lexer::new(&source); let tokens = match lexer.tokenize() { Ok(tokens) => tokens, @@ -38,7 +48,9 @@ fn main() { } }; - // println!("Tokens: {:?}", tokens); + if mode == "--debug" { + println!("Tokens: {:?}", tokens); + } // Step 2: Parse into AST let mut parser = Parser::new(tokens); @@ -50,13 +62,54 @@ fn main() { } }; - // Step 3: Print AST (debug output) - // println!("AST: {:#?}", ast); + if mode == "--debug" { + println!("AST: {:#?}", ast); + } - // Step 4: For now, just interpret - let mut codegen = CodeGenerator::new(); - if let Err(err) = codegen.interpret(&ast) { - eprintln!("Interpreter error: {}", err); - process::exit(1); + // Step 3: Execute based on mode + match mode { + "--interpret" => { + println!("Running in interpreter mode..."); + let context = Context::create(); + let mut codegen = match CodeGenerator::new(&context, "delta_module") { + Ok(cg) => cg, + Err(err) => { + eprintln!("Failed to create code generator: {}", err); + process::exit(1); + } + }; + + if let Err(err) = codegen.interpret(&ast) { + eprintln!("Interpreter error: {}", err); + process::exit(1); + } + } + "--compile" | _ => { + println!("Compiling to LLVM IR..."); + let context = Context::create(); + let mut codegen = match CodeGenerator::new(&context, "delta_module") { + Ok(cg) => cg, + Err(err) => { + eprintln!("Failed to create code generator: {}", err); + process::exit(1); + } + }; + + if let Err(err) = codegen.compile(&ast) { + eprintln!("Compilation error: {}", err); + process::exit(1); + } + + // Save LLVM IR to file + let ir_filename = filename.replace(".de", ".ll"); + if let Err(err) = codegen.save_to_file(&ir_filename) { + eprintln!("Failed to save LLVM IR: {}", err); + process::exit(1); + } + + println!("LLVM IR saved to: {}", ir_filename); + println!("To compile to executable, run:"); + println!(" clang {} -o {}", ir_filename, filename.replace(".de", "")); + } } }