From 0d7e67a8f234c77a9edbfcdc28460e8b992313cb Mon Sep 17 00:00:00 2001 From: Ianyourgod <104151459+Ianyourgod@users.noreply.github.com> Date: Fri, 5 Jul 2024 11:00:53 -0500 Subject: [PATCH] type system is better now. + i64 has been implemented. --- src/code_gen.rs | 30 +- .../{firstpass.rs => convert_pass.rs} | 266 ++++++++++-------- src/code_gen/inst_fixn2_pass.rs | 76 +++++ .../{thirdpass.rs => inst_fixup_pass.rs} | 79 ++++-- src/code_gen/nodes.rs | 34 ++- .../{secondpass.rs => pseudo_replace_pass.rs} | 34 ++- src/emitter.rs | 41 +-- src/lexer.rs | 2 +- src/main.rs | 4 +- src/parser.rs | 126 ++++----- src/parser/nodes.rs | 22 +- src/semantic_analysis.rs | 6 +- .../identifier_resolution_pass.rs | 65 +++-- src/semantic_analysis/symbol_table.rs | 9 +- src/semantic_analysis/type_checking_pass.rs | 223 +++++++++++---- src/tacky.rs | 198 ++++++++++--- src/tacky/nodes.rs | 103 ++++++- test.ag | 15 +- todo.md | 2 +- 19 files changed, 912 insertions(+), 423 deletions(-) rename src/code_gen/{firstpass.rs => convert_pass.rs} (73%) create mode 100644 src/code_gen/inst_fixn2_pass.rs rename src/code_gen/{thirdpass.rs => inst_fixup_pass.rs} (77%) rename src/code_gen/{secondpass.rs => pseudo_replace_pass.rs} (80%) diff --git a/src/code_gen.rs b/src/code_gen.rs index c0177a0..a6f4824 100644 --- a/src/code_gen.rs +++ b/src/code_gen.rs @@ -4,17 +4,19 @@ use crate::tacky; pub mod nodes; -mod firstpass; -mod secondpass; -mod thirdpass; +mod convert_pass; +mod pseudo_replace_pass; +mod inst_fixup_pass; +mod inst_fixn2_pass; pub struct CodeGen { pub program: tacky::nodes::Program, + symbol_table: tacky::nodes::SymbolTable, source: String } impl CodeGen { - pub fn new(program: tacky::nodes::Program, source: Option) -> CodeGen { + pub fn new(program: tacky::nodes::Program, symbol_table: tacky::nodes::SymbolTable, source: Option) -> CodeGen { let src: String; if source.is_some() { src = source.unwrap(); @@ -22,22 +24,22 @@ impl CodeGen { src = String::new(); } - CodeGen { program, source: src } + CodeGen { program, source: src, symbol_table } } pub fn generate_code(&mut self) -> nodes::Program { - let fp = firstpass::Pass::new(&self.program); + let first_pass = convert_pass::Pass::new(&self.program, self.symbol_table.clone()); + let first_pass_output = first_pass.run(); - let first_pass_output = fp.run(); + let mut second_pass = pseudo_replace_pass::Pass::new(&first_pass_output, self.symbol_table.clone()); + let second_pass_output = second_pass.run(); - let mut sp = secondpass::Pass::new(&first_pass_output); + let third_pass = inst_fixup_pass::Pass::new(&second_pass_output); + let third_pass_output = third_pass.run(); - let second_pass_output = sp.run(); + let fourth_pass = inst_fixn2_pass::Pass::new(&third_pass_output); + let fourth_pass_output = fourth_pass.run(); - let tp = thirdpass::Pass::new(&second_pass_output); - - let third_pass_output = tp.run(); - - third_pass_output + fourth_pass_output } } \ No newline at end of file diff --git a/src/code_gen/firstpass.rs b/src/code_gen/convert_pass.rs similarity index 73% rename from src/code_gen/firstpass.rs rename to src/code_gen/convert_pass.rs index 6e9d63b..6236c86 100644 --- a/src/code_gen/firstpass.rs +++ b/src/code_gen/convert_pass.rs @@ -8,11 +8,12 @@ const QUAD: &str = "q"; pub struct Pass { pub program: tacky::nodes::Program, + symbol_table: tacky::nodes::SymbolTable, } impl Pass { - pub fn new(program: &tacky::nodes::Program) -> Pass { - Pass { program: program.clone() } + pub fn new(program: &tacky::nodes::Program, symbol_table: tacky::nodes::SymbolTable) -> Pass { + Pass { program: program.clone(), symbol_table } } pub fn run(&self) -> nodes::Program { @@ -46,13 +47,13 @@ impl Pass { instructions.push(nodes::Instruction::Mov(nodes::BinOp { src: nodes::Operand::Register(reg), dest: nodes::Operand::Pseudo(nodes::Identifier { name: arg.0.clone() }), - suffix: Some(LONG.to_string()), + suffix: nodes::Suffix::L, })); } else { instructions.push(nodes::Instruction::Mov(nodes::BinOp { src: nodes::Operand::StackAllocate(-8 * (i - 6) as isize - 16), dest: nodes::Operand::Pseudo(nodes::Identifier { name: arg.0.clone() }), - suffix: Some(LONG.to_string()), + suffix: nodes::Suffix::L, })); } } @@ -71,7 +72,8 @@ impl Pass { fn convert_type(&self, ty: &tacky::nodes::Type) -> nodes::Type { match ty { - tacky::nodes::Type::Int => nodes::Type::Int, + tacky::nodes::Type::I32 => nodes::Type::I32, + tacky::nodes::Type::I64 => nodes::Type::I64, tacky::nodes::Type::Fn(ref args, ref ret) => { let mut arg_types = Vec::new(); for arg in args { @@ -83,6 +85,29 @@ impl Pass { } } + fn get_type(&self, value: &tacky::nodes::Value) -> tacky::nodes::Type { + match value { + tacky::nodes::Value::Identifier(ident) => { + self.symbol_table.get(ident).unwrap().clone() + } + tacky::nodes::Value::Constant(constant) => { + match constant { + tacky::nodes::Constant::I32(_) => tacky::nodes::Type::I32, + tacky::nodes::Constant::I64(_) => tacky::nodes::Type::I64, + } + } + _ => panic!("Unsupported value type"), + } + } + + fn type_to_suffix(&self, type_: &tacky::nodes::Type) -> nodes::Suffix { + match type_ { + tacky::nodes::Type::I32 => nodes::Suffix::L, + tacky::nodes::Type::I64 => nodes::Suffix::Q, + _ => panic!("Unsupported type"), + } + } + fn emit_instruction(&self, statement: &tacky::nodes::Instruction, instructions: &mut Vec) { match statement { tacky::nodes::Instruction::Return(return_value) => { @@ -90,7 +115,7 @@ impl Pass { instructions.push(nodes::Instruction::Mov(nodes::BinOp { src: value, dest: nodes::Operand::Register(nodes::Reg::AX), - suffix: Some(LONG.to_string()), + suffix: self.type_to_suffix(&self.get_type(return_value)), })); instructions.push(nodes::Instruction::Ret); } @@ -99,182 +124,163 @@ impl Pass { tacky::nodes::UnaryOperator::Negate => { let src = self.emit_value(&unary.src); let dest = self.emit_value(&unary.dest); + + let src_type = self.get_type(&unary.src); + instructions.push(nodes::Instruction::Mov(nodes::BinOp { src, dest: dest.clone(), - suffix: Some(LONG.to_string()), + suffix: self.type_to_suffix(&src_type), })); instructions.push(nodes::Instruction::Neg(nodes::UnaryOp { operand: dest, - suffix: Some(LONG.to_string()), + suffix: nodes::Suffix::L, })); } _ => panic!("Unsupported unary operator"), } } tacky::nodes::Instruction::Binary(binary) => { + let src1 = self.emit_value(&binary.src1); + let src2 = self.emit_value(&binary.src2); + let dest = self.emit_value(&binary.dest); + + let src1_type = self.get_type(&binary.src1); + let dest_type = self.get_type(&binary.dest); + let src_type_suf = self.type_to_suffix(&src1_type); + let dst_type_suf = self.type_to_suffix(&dest_type); + match binary.operator { tacky::nodes::BinaryOperator::Add => { - let src1 = self.emit_value(&binary.src1); - let src2 = self.emit_value(&binary.src2); - let dest = self.emit_value(&binary.dest); instructions.push(nodes::Instruction::Mov(nodes::BinOp { src: src1, dest: dest.clone(), - suffix: Some(LONG.to_string()), + suffix: src_type_suf.clone(), })); instructions.push(nodes::Instruction::Add(nodes::BinOp { src: src2, dest, - suffix: Some(LONG.to_string()), + suffix: src_type_suf.clone(), })); }, tacky::nodes::BinaryOperator::Subtract => { - let src1 = self.emit_value(&binary.src1); - let src2 = self.emit_value(&binary.src2); - let dest = self.emit_value(&binary.dest); - instructions.push(nodes::Instruction::Mov(nodes::BinOp { src: src1, dest: dest.clone(), - suffix: Some(LONG.to_string()), + suffix: src_type_suf.clone(), })); instructions.push(nodes::Instruction::Sub(nodes::BinOp { src: src2, dest, - suffix: Some(LONG.to_string()), + suffix: src_type_suf, })); }, tacky::nodes::BinaryOperator::Multiply => { - let src1 = self.emit_value(&binary.src1); - let src2 = self.emit_value(&binary.src2); - let dest = self.emit_value(&binary.dest); instructions.push(nodes::Instruction::Mov(nodes::BinOp { src: src1, dest: dest.clone(), - suffix: Some(LONG.to_string()), + suffix: src_type_suf.clone(), })); instructions.push(nodes::Instruction::Mul(nodes::BinOp { src: src2, dest, - suffix: Some(LONG.to_string()), + suffix: src_type_suf, })); }, tacky::nodes::BinaryOperator::Divide => { - let src1 = self.emit_value(&binary.src1); - let src2 = self.emit_value(&binary.src2); - let dest = self.emit_value(&binary.dest); instructions.push(nodes::Instruction::Mov(nodes::BinOp { src: src1, dest: nodes::Operand::Register(nodes::Reg::AX), - suffix: Some(LONG.to_string()), + suffix: src_type_suf.clone(), })); - instructions.push(nodes::Instruction::Cdq); + instructions.push(nodes::Instruction::Cdq(src_type_suf.clone())); instructions.push(nodes::Instruction::Div(nodes::UnaryOp { operand: src2, - suffix: Some(LONG.to_string()), + suffix: src_type_suf.clone(), })); instructions.push(nodes::Instruction::Mov(nodes::BinOp { src: nodes::Operand::Register(nodes::Reg::AX), dest, - suffix: Some(LONG.to_string()), + suffix: src_type_suf, })); }, tacky::nodes::BinaryOperator::And => panic!(), tacky::nodes::BinaryOperator::Or => panic!(), tacky::nodes::BinaryOperator::GreaterThan => { - let src1 = self.emit_value(&binary.src1); - let src2 = self.emit_value(&binary.src2); - let dest = self.emit_value(&binary.dest); instructions.push(nodes::Instruction::Cmp(nodes::BinOp { src: src1, dest: src2.clone(), - suffix: Some(LONG.to_string()), + suffix: src_type_suf, })); instructions.push(nodes::Instruction::Mov(nodes::BinOp { src: nodes::Operand::Immediate(0), dest: dest.clone(), - suffix: Some(LONG.to_string()), + suffix: dst_type_suf, })); instructions.push(nodes::Instruction::SetCC(nodes::CondCode::G, dest)); }, tacky::nodes::BinaryOperator::GreaterThanEqual => { - let src1 = self.emit_value(&binary.src1); - let src2 = self.emit_value(&binary.src2); - let dest = self.emit_value(&binary.dest); instructions.push(nodes::Instruction::Cmp(nodes::BinOp { src: src1, dest: src2.clone(), - suffix: Some(LONG.to_string()), + suffix: src_type_suf, })); instructions.push(nodes::Instruction::Mov(nodes::BinOp { src: nodes::Operand::Immediate(0), dest: dest.clone(), - suffix: Some(LONG.to_string()), + suffix: dst_type_suf, })); instructions.push(nodes::Instruction::SetCC(nodes::CondCode::GE, dest)); }, tacky::nodes::BinaryOperator::LessThan => { - let src1 = self.emit_value(&binary.src1); - let src2 = self.emit_value(&binary.src2); - let dest = self.emit_value(&binary.dest); instructions.push(nodes::Instruction::Cmp(nodes::BinOp { src: src1, dest: src2.clone(), - suffix: Some(LONG.to_string()), + suffix: src_type_suf, })); instructions.push(nodes::Instruction::Mov(nodes::BinOp { src: nodes::Operand::Immediate(0), dest: dest.clone(), - suffix: Some(LONG.to_string()), + suffix: dst_type_suf, })); instructions.push(nodes::Instruction::SetCC(nodes::CondCode::L, dest)); }, tacky::nodes::BinaryOperator::LessThanEqual => { - let src1 = self.emit_value(&binary.src1); - let src2 = self.emit_value(&binary.src2); - let dest = self.emit_value(&binary.dest); instructions.push(nodes::Instruction::Cmp(nodes::BinOp { src: src1, dest: src2.clone(), - suffix: Some(LONG.to_string()), + suffix: src_type_suf, })); instructions.push(nodes::Instruction::Mov(nodes::BinOp { src: nodes::Operand::Immediate(0), dest: dest.clone(), - suffix: Some(LONG.to_string()), + suffix: dst_type_suf, })); instructions.push(nodes::Instruction::SetCC(nodes::CondCode::LE, dest)); }, tacky::nodes::BinaryOperator::Equal => { - let src1 = self.emit_value(&binary.src1); - let src2 = self.emit_value(&binary.src2); - let dest = self.emit_value(&binary.dest); instructions.push(nodes::Instruction::Cmp(nodes::BinOp { src: src1, dest: src2.clone(), - suffix: Some(LONG.to_string()), + suffix: src_type_suf, })); instructions.push(nodes::Instruction::Mov(nodes::BinOp { src: nodes::Operand::Immediate(0), dest: dest.clone(), - suffix: Some(LONG.to_string()), + suffix: dst_type_suf, })); instructions.push(nodes::Instruction::SetCC(nodes::CondCode::E, dest)); }, tacky::nodes::BinaryOperator::NotEqual => { - let src1 = self.emit_value(&binary.src1); - let src2 = self.emit_value(&binary.src2); - let dest = self.emit_value(&binary.dest); instructions.push(nodes::Instruction::Cmp(nodes::BinOp { src: src1, dest: src2.clone(), - suffix: Some(LONG.to_string()), + suffix: src_type_suf, })); instructions.push(nodes::Instruction::Mov(nodes::BinOp { src: nodes::Operand::Immediate(0), dest: dest.clone(), - suffix: Some(LONG.to_string()), + suffix: dst_type_suf, })); instructions.push(nodes::Instruction::SetCC(nodes::CondCode::NE, dest)); }, @@ -283,21 +289,28 @@ impl Pass { tacky::nodes::Instruction::Copy(copy) => { let src = self.emit_value(©.src); let dest = self.emit_value(©.dest); + + let src_type_suffix = self.type_to_suffix(&self.get_type(©.src)); + instructions.push(nodes::Instruction::Mov(nodes::BinOp { src, dest, - suffix: Some(LONG.to_string()), + suffix: src_type_suffix, })); } tacky::nodes::Instruction::Jump(label) => { instructions.push(nodes::Instruction::Jump(label.clone())); } tacky::nodes::Instruction::JumpIfZero(label, value) => { + let value_type = self.get_type(value); + let value_type_suffix = self.type_to_suffix(&value_type); + let value = self.emit_value(value); + instructions.push(nodes::Instruction::Cmp(nodes::BinOp { src: nodes::Operand::Immediate(0), dest: value.clone(), - suffix: Some(LONG.to_string()), + suffix: value_type_suffix, })); instructions.push(nodes::Instruction::JumpCC(nodes::CondCode::E, label.clone())); } @@ -306,7 +319,7 @@ impl Pass { instructions.push(nodes::Instruction::Cmp(nodes::BinOp { src: nodes::Operand::Immediate(0), dest: value.clone(), - suffix: Some(LONG.to_string()), + suffix: nodes::Suffix::L, })); instructions.push(nodes::Instruction::JumpCC(nodes::CondCode::NE, label.clone())); } @@ -316,14 +329,14 @@ impl Pass { tacky::nodes::Instruction::FunCall(fun_call) => { let arg_registers = vec![nodes::Reg::DI, nodes::Reg::SI, nodes::Reg::DX, nodes::Reg::CX, nodes::Reg::R8, nodes::Reg::R9]; - let mut register_args: Vec = Vec::new(); - let mut stack_args: Vec = Vec::new(); + let mut register_args: Vec = Vec::new(); + let mut stack_args: Vec = Vec::new(); for (i, arg) in fun_call.arguments.iter().enumerate() { if i < 6 { - register_args.push(self.emit_value(arg)); + register_args.push(arg.clone()); } else { - stack_args.push(self.emit_value(arg)); + stack_args.push(arg.clone()); } } @@ -335,37 +348,34 @@ impl Pass { for (i, arg) in register_args.iter().enumerate() { instructions.push(nodes::Instruction::Mov(nodes::BinOp { - src: arg.clone(), + src: self.emit_value(arg), dest: nodes::Operand::Register(arg_registers[i]), - suffix: Some(LONG.to_string()), + suffix: self.type_to_suffix(&self.get_type(arg)) })); } // go over stack args in reverse order for (i, arg) in stack_args.iter().enumerate().rev() { - match arg { - nodes::Operand::Immediate(_) => { - instructions.push(nodes::Instruction::Push(nodes::UnaryOp { - operand: arg.clone(), - suffix: Some(QUAD.to_string()), - })); - }, - nodes::Operand::Register(_) => { - instructions.push(nodes::Instruction::Push(nodes::UnaryOp { - operand: arg.clone(), - suffix: Some(QUAD.to_string()), - })); - }, + let assembly_arg = self.emit_value(arg); + + let type_ = self.get_type(arg); + + if type_ == tacky::nodes::Type::I64 { + instructions.push(nodes::Instruction::Push(assembly_arg)); + return; + } + + match assembly_arg { + nodes::Operand::Register(_) | nodes::Operand::Immediate(_) => { + instructions.push(nodes::Instruction::Push(assembly_arg)); + } neither => { instructions.push(nodes::Instruction::Mov(nodes::BinOp { src: neither.clone(), dest: nodes::Operand::Register(nodes::Reg::AX), - suffix: Some(LONG.to_string()), - })); - instructions.push(nodes::Instruction::Push(nodes::UnaryOp { - operand: nodes::Operand::Register(nodes::Reg::AX), - suffix: Some(QUAD.to_string()), + suffix: nodes::Suffix::L, })); + instructions.push(nodes::Instruction::Push(nodes::Operand::Register(nodes::Reg::AX))); } } } @@ -381,25 +391,47 @@ impl Pass { let dst = self.emit_value(&fun_call.dest); + let dst_type_suffix = self.type_to_suffix(&self.get_type(&fun_call.dest)); + instructions.push(nodes::Instruction::Mov(nodes::BinOp { src: nodes::Operand::Register(nodes::Reg::AX), dest: dst, - suffix: Some(LONG.to_string()), + suffix: dst_type_suffix, })); - } + }, + tacky::nodes::Instruction::SignExtend(src, dst) => { + let src = self.emit_value(src); + let dst = self.emit_value(dst); + instructions.push(nodes::Instruction::Movsx(src, dst)); + }, + tacky::nodes::Instruction::Truncate(src, dst) => { + let src = self.emit_value(src); + let dst = self.emit_value(dst); + + instructions.push(nodes::Instruction::Mov(nodes::BinOp { + src, + dest: dst, + suffix: nodes::Suffix::L, + })); + }, } } fn emit_value(&self, value: &tacky::nodes::Value) -> nodes::Operand { - match value { - tacky::nodes::Value::Identifier(identifier) => { - nodes::Operand::Pseudo(nodes::Identifier { name: identifier.clone() }) - } - tacky::nodes::Value::Constant(constant) => { - nodes::Operand::Immediate(*constant) + if value.is_identifier() { + let identifier = value.as_identifier(); + return nodes::Operand::Pseudo(nodes::Identifier { name: identifier.to_string() }) + } + + if value.is_constant() { + let constant = value.as_constant(); + return if constant.is_i32() { + nodes::Operand::Immediate(constant.as_i32() as i64) + } else { + nodes::Operand::Immediate(constant.as_i64()) } - _ => panic!("mmm this shouldnt happen you fucked up") } + panic!("Unsupported value type"); } } @@ -415,10 +447,10 @@ mod tests { function_definitions: vec![ tacky::nodes::FunctionDefinition { function_name: "main".to_string(), - return_type: tacky::nodes::Type::Int, + return_type: tacky::nodes::Type::I32, body: CompoundInstruction { instructions: vec![ - tacky::nodes::Instruction::Return(tacky::nodes::Value::Constant(0)) + tacky::nodes::Instruction::Return(tacky::nodes::Value::Constant(tacky::nodes::Constant::I32(0))) ] }, arguments: Vec::new() @@ -426,18 +458,18 @@ mod tests { ] }; - let pass = Pass::new(&tack); + let pass = Pass::new(&tack, tacky::nodes::SymbolTable::new()); let program = pass.run(); assert_eq!(program.statements.len(), 1); assert_eq!(program.statements[0].function_name, "main".to_string()); - assert_eq!(program.statements[0].return_type, nodes::Type::Int); + assert_eq!(program.statements[0].return_type, nodes::Type::I32); assert_eq!(program.statements[0].context, nodes::Context { var_map: std::collections::HashMap::new(), stack_offset: 4 }); assert_eq!(program.statements[0].instructions.len(), 2); assert_eq!(program.statements[0].instructions[0], nodes::Instruction::Mov(nodes::BinOp { src: nodes::Operand::Immediate(0), dest: nodes::Operand::Register(nodes::Reg::AX), - suffix: Some(LONG.to_string()), + suffix: nodes::Suffix::L, })); assert_eq!(program.statements[0].instructions[1], nodes::Instruction::Ret); } @@ -448,12 +480,12 @@ mod tests { function_definitions: vec![ tacky::nodes::FunctionDefinition { function_name: "main".to_string(), - return_type: tacky::nodes::Type::Int, + return_type: tacky::nodes::Type::I32, body: CompoundInstruction { instructions: vec![ tacky::nodes::Instruction::Unary(tacky::nodes::Unary { operator: tacky::nodes::UnaryOperator::Negate, - src: tacky::nodes::Value::Constant(1), + src: tacky::nodes::Value::Constant(tacky::nodes::Constant::I32(0)), dest: tacky::nodes::Value::Identifier("a".to_string()), }) ] @@ -463,12 +495,16 @@ mod tests { ] }; - let pass = Pass::new(&tack); + let mut sym_tbl = tacky::nodes::SymbolTable::new(); + + sym_tbl.insert("a".to_string(), tacky::nodes::Type::I32); + + let pass = Pass::new(&tack, sym_tbl); let program = pass.run(); assert_eq!(program.statements.len(), 1); assert_eq!(program.statements[0].function_name, "main".to_string()); - assert_eq!(program.statements[0].return_type, nodes::Type::Int); + assert_eq!(program.statements[0].return_type, nodes::Type::I32); assert_eq!(program.statements[0].context, nodes::Context { var_map: std::collections::HashMap::new(), stack_offset: 4 }); } @@ -478,13 +514,13 @@ mod tests { function_definitions: vec![ tacky::nodes::FunctionDefinition { function_name: "main".to_string(), - return_type: tacky::nodes::Type::Int, + return_type: tacky::nodes::Type::I32, body: CompoundInstruction { instructions: vec![ tacky::nodes::Instruction::Binary(tacky::nodes::Binary { operator: tacky::nodes::BinaryOperator::Add, - src1: tacky::nodes::Value::Constant(1), - src2: tacky::nodes::Value::Constant(2), + src1: tacky::nodes::Value::Constant(tacky::nodes::Constant::I32(1)), + src2: tacky::nodes::Value::Constant(tacky::nodes::Constant::I32(2)), dest: tacky::nodes::Value::Identifier("a".to_string()), }) ] @@ -494,24 +530,28 @@ mod tests { ] }; - let pass = Pass::new(&tack); + let mut sym_tbl = tacky::nodes::SymbolTable::new(); + + sym_tbl.insert("a".to_string(), tacky::nodes::Type::I32); + + let pass = Pass::new(&tack, sym_tbl); let program = pass.run(); assert_eq!(program.statements.len(), 1); assert_eq!(program.statements[0].function_name, "main".to_string()); - assert_eq!(program.statements[0].return_type, nodes::Type::Int); + assert_eq!(program.statements[0].return_type, nodes::Type::I32); assert_eq!(program.statements[0].context, nodes::Context { var_map: std::collections::HashMap::new(), stack_offset: 4 }); assert_eq!(program.statements[0].instructions.len(), 2); assert_eq!(program.statements[0].instructions[0], nodes::Instruction::Mov(nodes::BinOp { src: nodes::Operand::Immediate(1), dest: nodes::Operand::Pseudo(nodes::Identifier { name: "a".to_string() }), - suffix: Some(LONG.to_string()), + suffix: nodes::Suffix::L, })); assert_eq!(program.statements[0].instructions[1], nodes::Instruction::Add(nodes::BinOp { src: nodes::Operand::Immediate(2), dest: nodes::Operand::Pseudo(nodes::Identifier { name: "a".to_string() }), - suffix: Some(LONG.to_string()), + suffix: nodes::Suffix::L, })); } } \ No newline at end of file diff --git a/src/code_gen/inst_fixn2_pass.rs b/src/code_gen/inst_fixn2_pass.rs new file mode 100644 index 0000000..23df736 --- /dev/null +++ b/src/code_gen/inst_fixn2_pass.rs @@ -0,0 +1,76 @@ +use crate::code_gen::nodes; + +// all this does is make sure that the mov doesnt have an immediate thats too big (i.e. 32 bits) + +pub struct Pass { + pub program: nodes::Program, +} + +impl Pass { + pub fn new(program: &nodes::Program) -> Pass { + Pass { program: program.clone() } + } + + pub fn run(&self) -> nodes::Program { + let mut program = nodes::Program { + statements: Vec::new(), + }; + + for function in self.program.statements.clone() { + let mut instructions: Vec = Vec::new(); + + instructions.push(nodes::Instruction::AllocateStack((function.context.stack_offset + 15) & !15)); // Align stack to 16 bytes + + for statement in function.instructions { + self.emit_instruction(&statement, &mut instructions); + } + + program.statements.push(nodes::FunctionDefinition::new( + function.function_name.clone(), + instructions, + function.context, + function.return_type.clone(), + )); + } + + program + } + + fn arg_is_immediate(&self, arg: &nodes::Operand) -> (bool, i64) { + match arg { + nodes::Operand::Immediate(val) => (true, *val), + _ => (false, 0), + } + } + + fn emit_instruction(&self, statement: &nodes::Instruction, instructions: &mut Vec) { + match statement { + nodes::Instruction::Mov(ref mov) => { + let src_is_immediate = self.arg_is_immediate(&mov.src); + let dest_is_memory = match &mov.dest { + nodes::Operand::StackAllocate(_) => true, + _ => false, + }; + + if src_is_immediate.0 && src_is_immediate.1 > i32::MAX as i64 && dest_is_memory { + instructions.push(nodes::Instruction::Mov(nodes::BinOp { + dest: nodes::Operand::Register(nodes::Reg::R10), + src: mov.src.clone(), + suffix: mov.suffix.clone(), + })); + + instructions.push(nodes::Instruction::Mov(nodes::BinOp { + dest: mov.dest.clone(), + src: nodes::Operand::Register(nodes::Reg::R10), + suffix: mov.suffix.clone(), + })); + } else { + instructions.push(statement.clone()); + } + }, + _ => { + instructions.push(statement.clone()); + }, + } + } +} \ No newline at end of file diff --git a/src/code_gen/thirdpass.rs b/src/code_gen/inst_fixup_pass.rs similarity index 77% rename from src/code_gen/thirdpass.rs rename to src/code_gen/inst_fixup_pass.rs index f40b8cf..8c0a2cc 100644 --- a/src/code_gen/thirdpass.rs +++ b/src/code_gen/inst_fixup_pass.rs @@ -67,12 +67,12 @@ impl Pass { instructions.push(nodes::Instruction::Mov(nodes::BinOp { dest: nodes::Operand::Register(nodes::Reg::R10), src: nodes::Operand::StackAllocate(idx2), - suffix: Some("l".to_string()), + suffix: mov.suffix.clone(), })); instructions.push(nodes::Instruction::Mov(nodes::BinOp { dest: nodes::Operand::StackAllocate(idx1), src: nodes::Operand::Register(nodes::Reg::R10), - suffix: Some("l".to_string()), + suffix: mov.suffix.clone(), })); return; } @@ -89,12 +89,12 @@ impl Pass { instructions.push(nodes::Instruction::Mov(nodes::BinOp { dest: nodes::Operand::Register(nodes::Reg::R10), src: nodes::Operand::StackAllocate(idx2), - suffix: Some("l".to_string()), + suffix: add.suffix.clone(), })); instructions.push(nodes::Instruction::Add(nodes::BinOp { dest: nodes::Operand::StackAllocate(idx1), src: nodes::Operand::Register(nodes::Reg::R10), - suffix: Some("l".to_string()), + suffix: add.suffix.clone(), })); return; } @@ -111,12 +111,12 @@ impl Pass { instructions.push(nodes::Instruction::Mov(nodes::BinOp { dest: nodes::Operand::Register(nodes::Reg::R10), src: nodes::Operand::StackAllocate(idx2), - suffix: Some("l".to_string()), + suffix: sub.suffix.clone(), })); instructions.push(nodes::Instruction::Sub(nodes::BinOp { dest: nodes::Operand::StackAllocate(idx1), src: nodes::Operand::Register(nodes::Reg::R10), - suffix: Some("l".to_string()), + suffix: sub.suffix.clone(), })); return; } @@ -133,22 +133,22 @@ impl Pass { instructions.push(nodes::Instruction::Mov(nodes::BinOp { dest: nodes::Operand::Register(nodes::Reg::R10), src: nodes::Operand::StackAllocate(idx2), - suffix: Some("l".to_string()), + suffix: mul.suffix.clone(), })); instructions.push(nodes::Instruction::Mov(nodes::BinOp { dest: nodes::Operand::Register(nodes::Reg::R11), src: nodes::Operand::StackAllocate(idx1), - suffix: Some("l".to_string()), + suffix: mul.suffix.clone(), })); instructions.push(nodes::Instruction::Mul(nodes::BinOp { dest: nodes::Operand::Register(nodes::Reg::R11), src: nodes::Operand::Register(nodes::Reg::R10), - suffix: Some("l".to_string()), + suffix: mul.suffix.clone(), })); instructions.push(nodes::Instruction::Mov(nodes::BinOp { dest: nodes::Operand::StackAllocate(idx1), src: nodes::Operand::Register(nodes::Reg::R11), - suffix: Some("l".to_string()), + suffix: mul.suffix.clone(), })); return; } @@ -157,7 +157,7 @@ impl Pass { instructions.push(nodes::Instruction::Mov(nodes::BinOp { dest: nodes::Operand::Register(nodes::Reg::R11), src: nodes::Operand::StackAllocate(*idx1), - suffix: Some("l".to_string()), + suffix: mul.suffix.clone(), })); instructions.push(nodes::Instruction::Mul(nodes::BinOp { dest: nodes::Operand::Register(nodes::Reg::R11), @@ -167,7 +167,7 @@ impl Pass { instructions.push(nodes::Instruction::Mov(nodes::BinOp { dest: nodes::Operand::StackAllocate(*idx1), src: nodes::Operand::Register(nodes::Reg::R11), - suffix: Some("l".to_string()), + suffix: mul.suffix.clone(), })); return; } @@ -183,7 +183,7 @@ impl Pass { instructions.push(nodes::Instruction::Mov(nodes::BinOp { dest: nodes::Operand::Register(nodes::Reg::R10), src: nodes::Operand::Immediate(*val), - suffix: Some("l".to_string()), + suffix: div.suffix.clone(), })); instructions.push(nodes::Instruction::Div(nodes::UnaryOp { operand: nodes::Operand::Register(nodes::Reg::R10), @@ -201,25 +201,25 @@ impl Pass { instructions.push(nodes::Instruction::Mov(nodes::BinOp { dest: nodes::Operand::Register(nodes::Reg::R10), src: nodes::Operand::StackAllocate(idx2), - suffix: Some("l".to_string()), + suffix: cmp.suffix.clone(), })); if dest_is_immediate { instructions.push(nodes::Instruction::Mov(nodes::BinOp { dest: nodes::Operand::Register(nodes::Reg::R11), src: nodes::Operand::StackAllocate(idx1), - suffix: Some("l".to_string()), + suffix: cmp.suffix.clone(), })); instructions.push(nodes::Instruction::Cmp(nodes::BinOp { dest: nodes::Operand::Register(nodes::Reg::R11), src: nodes::Operand::Register(nodes::Reg::R10), - suffix: Some("l".to_string()), + suffix: cmp.suffix.clone(), })); return; } instructions.push(nodes::Instruction::Cmp(nodes::BinOp { dest: nodes::Operand::StackAllocate(idx1), src: nodes::Operand::Register(nodes::Reg::R10), - suffix: Some("l".to_string()), + suffix: cmp.suffix.clone(), })); return; } @@ -228,18 +228,57 @@ impl Pass { instructions.push(nodes::Instruction::Mov(nodes::BinOp { dest: nodes::Operand::Register(nodes::Reg::R11), src: cmp.src.clone(), - suffix: Some("l".to_string()), + suffix: cmp.suffix.clone(), })); instructions.push(nodes::Instruction::Cmp(nodes::BinOp { dest: nodes::Operand::Register(nodes::Reg::R11), src: cmp.dest.clone(), - suffix: Some("l".to_string()), + suffix: cmp.suffix.clone(), })); return; } instructions.push(statement.clone()); - } + }, + nodes::Instruction::Movsx(src, dst) => { + let src_is_immediate = self.arg_is_immediate(src); + let dst_is_mem = match dst { + nodes::Operand::StackAllocate(_) => true, + _ => false, + }; + + if src_is_immediate { + instructions.push(nodes::Instruction::Mov(nodes::BinOp { + dest: nodes::Operand::Register(nodes::Reg::R10), + src: src.clone(), + suffix: nodes::Suffix::L, + })); + + if dst_is_mem { + instructions.push(nodes::Instruction::Movsx(nodes::Operand::Register(nodes::Reg::R10), nodes::Operand::Register(nodes::Reg::R11))); + instructions.push(nodes::Instruction::Mov(nodes::BinOp { + src: nodes::Operand::Register(nodes::Reg::R11), + dest: dst.clone(), + suffix: nodes::Suffix::Q, + })); + return; + } + + instructions.push(nodes::Instruction::Movsx(nodes::Operand::Register(nodes::Reg::R10), dst.clone())); + } + + if dst_is_mem { + instructions.push(nodes::Instruction::Movsx(src.clone(), nodes::Operand::Register(nodes::Reg::R11))); + instructions.push(nodes::Instruction::Mov(nodes::BinOp { + src: nodes::Operand::Register(nodes::Reg::R11), + dest: dst.clone(), + suffix: nodes::Suffix::Q, + })); + return; + } + + instructions.push(nodes::Instruction::Movsx(src.clone(), dst.clone())); + }, _ => { instructions.push(statement.clone()); }, diff --git a/src/code_gen/nodes.rs b/src/code_gen/nodes.rs index 5489a1c..15012fb 100644 --- a/src/code_gen/nodes.rs +++ b/src/code_gen/nodes.rs @@ -1,6 +1,7 @@ #![allow(dead_code)] use std::collections::HashMap; +use std::fmt::Display; #[derive(Debug, Clone, PartialEq)] pub struct Program { @@ -35,7 +36,8 @@ pub struct FunctionDefinition { #[derive(Debug, Clone, PartialEq)] pub enum Type { - Int, + I32, + I64, Fn(Vec, Box), Identifier(String), } @@ -58,17 +60,18 @@ impl FunctionDefinition { #[derive(Debug, Clone, PartialEq)] pub enum Instruction { Mov(BinOp), + Movsx(Operand, Operand), Ret, AllocateStack(usize), DeallocateStack(usize), - Push(UnaryOp), + Push(Operand), Pop(UnaryOp), Add(BinOp), Sub(BinOp), Mul(BinOp), Div(UnaryOp), Neg(UnaryOp), - Cdq, + Cdq(Suffix), Label(String), Cmp(BinOp), Jump(String), @@ -91,19 +94,19 @@ pub enum CondCode { pub struct BinOp { pub dest: Operand, pub src: Operand, - pub suffix: Option, + pub suffix: Suffix, } #[derive(Debug, Clone, PartialEq)] pub struct UnaryOp { pub operand: Operand, - pub suffix: Option, + pub suffix: Suffix, } #[derive(Debug, Clone, PartialEq)] pub enum Operand { Register(Reg), - Immediate(i32), + Immediate(i64), StackAllocate(isize), Pseudo(Identifier), } @@ -126,6 +129,25 @@ pub enum Reg { R11 } +#[derive(Debug, Clone, PartialEq)] +pub enum Suffix { + B, + W, + L, + Q, +} + +impl Display for Suffix { + fn fmt(&self, f: &mut std::fmt::Formatter) -> core::fmt::Result { + match self { + Suffix::B => write!(f, "b"), + Suffix::W => write!(f, "w"), + Suffix::L => write!(f, "l"), + Suffix::Q => write!(f, "q"), + } + } +} + fn reg_to_str(reg: Reg, size: Option) -> String { format!("%{}", match reg { Reg::AX => { diff --git a/src/code_gen/secondpass.rs b/src/code_gen/pseudo_replace_pass.rs similarity index 80% rename from src/code_gen/secondpass.rs rename to src/code_gen/pseudo_replace_pass.rs index 4e18100..8d9a848 100644 --- a/src/code_gen/secondpass.rs +++ b/src/code_gen/pseudo_replace_pass.rs @@ -1,12 +1,14 @@ use crate::code_gen::nodes; +use crate::tacky; pub struct Pass { pub program: nodes::Program, + symbol_table: tacky::nodes::SymbolTable, } impl Pass { - pub fn new(program: &nodes::Program) -> Pass { - Pass { program: program.clone() } + pub fn new(program: &nodes::Program, symbol_table: tacky::nodes::SymbolTable) -> Pass { + Pass { program: program.clone(), symbol_table } } pub fn run(&mut self) -> nodes::Program { @@ -109,13 +111,16 @@ impl Pass { instructions.push(nodes::Instruction::SetCC(cond_code.clone(), operand)); }, - nodes::Instruction::Push(ref push) => { - let operand = self.emit_operand(&push.operand, instructions, context); + nodes::Instruction::Push(ref operand) => { + let operand = self.emit_operand(&operand, instructions, context); - instructions.push(nodes::Instruction::Push(nodes::UnaryOp { - operand, - suffix: push.suffix.clone(), - })); + instructions.push(nodes::Instruction::Push(operand)); + }, + nodes::Instruction::Movsx(src, dst) => { + let src = self.emit_operand(&src, instructions, context); + let dst = self.emit_operand(&dst, instructions, context); + + instructions.push(nodes::Instruction::Movsx(src, dst)); }, _ => { instructions.push(statement.clone()); @@ -132,7 +137,18 @@ impl Pass { nodes::Operand::StackAllocate(*offset as isize) }, None => { - context.stack_offset += 4; + let size = if self.symbol_table.get(&identifier.name).unwrap() == &tacky::nodes::Type::I32 { + 4 + } else { + 8 + }; + + let offset = context.stack_offset % 8; + + if size == 8 && offset != 0 { + context.stack_offset += 8 - offset; + } + context.stack_offset += size; context.var_map.insert(identifier.name.clone(), context.stack_offset); nodes::Operand::StackAllocate(context.stack_offset as isize) }, diff --git a/src/emitter.rs b/src/emitter.rs index 43f486d..edc8589 100644 --- a/src/emitter.rs +++ b/src/emitter.rs @@ -13,27 +13,21 @@ impl Emitter { } } - fn get_size_of_suffix(&self, suffix: &Option) -> u8 { + fn get_size_of_suffix(&self, suffix: &code_gen::nodes::Suffix) -> u8 { match suffix { - Some(s) => { - match s.as_str() { - "b" => 1, - "w" => 2, - "l" => 4, - "q" => 8, - _ => panic!() - } - } - None => 4 + code_gen::nodes::Suffix::B => 1, + code_gen::nodes::Suffix::W => 2, + code_gen::nodes::Suffix::L => 4, + code_gen::nodes::Suffix::Q => 8, } } - fn displ_op(&self, operand: &code_gen::nodes::Operand, suffix: &Option) -> String { + fn displ_op(&self, operand: &code_gen::nodes::Operand, suffix: &code_gen::nodes::Suffix) -> String { operand.displ(Some(self.get_size_of_suffix(suffix))) } - fn get_suffix(&self, suffix: &Option) -> String { - if suffix.is_some() {suffix.clone().unwrap()} else {String::new()} + fn get_suffix(&self, suffix: &code_gen::nodes::Suffix) -> String { + format!("{}", suffix) } fn cond_code_to_str(&self, cond_code: &code_gen::nodes::CondCode) -> String { @@ -58,14 +52,14 @@ impl Emitter { match instruction { code_gen::nodes::Instruction::Mov(mov) => { - output.push_str(&format!(" mov{} {}, {}\n", if mov.suffix.is_some() {mov.suffix.clone().unwrap()} else {String::new()}, self.displ_op(&mov.src, &mov.suffix), self.displ_op(&mov.dest, &mov.suffix))); + output.push_str(&format!(" mov{} {}, {}\n", mov.suffix, self.displ_op(&mov.src, &mov.suffix), self.displ_op(&mov.dest, &mov.suffix))); } code_gen::nodes::Instruction::Ret => { output.push_str(" leave\n"); output.push_str(" ret\n"); } - code_gen::nodes::Instruction::Push(push) => { - output.push_str(&format!(" push{} {}\n", self.get_suffix(&push.suffix), self.displ_op(&push.operand, &push.suffix))); + code_gen::nodes::Instruction::Push(operand) => { + output.push_str(&format!(" push {}\n", self.displ_op(&operand, &code_gen::nodes::Suffix::Q))); } code_gen::nodes::Instruction::Pop(pop) => { output.push_str(&format!(" pop{} {}\n", self.get_suffix(&pop.suffix), self.displ_op(&pop.operand, &pop.suffix))); @@ -85,8 +79,12 @@ impl Emitter { code_gen::nodes::Instruction::Neg(neg) => { output.push_str(&format!(" neg{} {}\n", self.get_suffix(&neg.suffix), self.displ_op(&neg.operand, &neg.suffix))); } - code_gen::nodes::Instruction::Cdq => { - output.push_str(" cdq\n"); + code_gen::nodes::Instruction::Cdq(suffix) => { + output.push_str(if suffix==&code_gen::nodes::Suffix::L { + " cqd\n" + } else { + " cqt\n" + }); } code_gen::nodes::Instruction::AllocateStack(allocate_stack) => { output.push_str(&format!(" sub ${}, %rsp\n", allocate_stack)); @@ -107,11 +105,14 @@ impl Emitter { output.push_str(&format!(" j{} {}\n", self.cond_code_to_str(cond_code), jump)); } code_gen::nodes::Instruction::SetCC(cond_code, operand) => { - output.push_str(&format!(" set{} {}\n", self.cond_code_to_str(cond_code), self.displ_op(operand, &Some("b".to_string())))); + output.push_str(&format!(" set{} {}\n", self.cond_code_to_str(cond_code), self.displ_op(operand, &code_gen::nodes::Suffix::B))); } code_gen::nodes::Instruction::Call(call) => { output.push_str(&format!(" call {}\n", call)); } + code_gen::nodes::Instruction::Movsx(src, dst) => { + output.push_str(&format!(" movsx {}, {}\n", self.displ_op(src, &code_gen::nodes::Suffix::L), self.displ_op(dst, &code_gen::nodes::Suffix::Q))); + } //#[allow(unreachable_patterns)] //_ => panic!() } diff --git a/src/lexer.rs b/src/lexer.rs index e7e0295..70c4dfa 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -302,7 +302,7 @@ impl Lexer { self.read_char(); } - let keywords = vec!["fn", "let", "return", "if", "else", "while", "break", "continue", "int", "i64"]; + let keywords = vec!["fn", "let", "return", "if", "else", "while", "break", "continue", "int", "i32", "i64"]; // todo: remove int from keywords and improve the type system let literal = self.input[position..self.position].to_string(); diff --git a/src/main.rs b/src/main.rs index ed42009..51af0c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,13 +31,13 @@ fn compile_program(input: String, input_name: String, outfile_name: &String, inc let program = parser.parse_program(); let mut resolver = semantic_analysis::Analysis::new(program.clone()); - let program = resolver.run(); + let (program, symbol_table) = resolver.run(); let mut tacky = tacky::Tacky::new(program); let program = tacky.generate(); - let mut compiler = code_gen::CodeGen::new(program, Some(input)); + let mut compiler = code_gen::CodeGen::new(program, tacky.symbol_table, Some(input)); let assembly_asm = compiler.generate_code(); let emitter = emitter::Emitter::new(assembly_asm); diff --git a/src/parser.rs b/src/parser.rs index 09c0e6b..5b6934a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -24,7 +24,7 @@ impl Parser { } } - fn error(&mut self, error_message: String, line: usize, position: usize, length: usize, error_code: Option) { + fn error(&self, error_message: String, line: usize, position: usize, length: usize, error_code: Option) { if self.error_func.is_some() { self.error_func.unwrap()(self.input_name.clone(), self.lexer.input.clone(), error_message, line, position, length, error_code); return; @@ -95,6 +95,29 @@ impl Parser { program } + fn valid_type(&self) -> nodes::Type { + let builtin_types = vec!["int", "i32", "i64"]; + + if self.cur_token.kind != lexer::TokenType::Keyword { + self.error(format!("Expected type, found {:#?}", self.cur_token.kind), self.cur_token.line, self.cur_token.pos, self.cur_token.length, Some(1)); + } + + if builtin_types.contains(&self.cur_token.literal.as_str()) { + match self.cur_token.literal.as_str() { + "int" => nodes::Type::I32, + "i32" => nodes::Type::I32, + "i64" => nodes::Type::I64, + _ => { + self.error(format!("Unknown type: {}", self.cur_token.literal), self.cur_token.line, self.cur_token.pos, self.cur_token.length, Some(1)); + panic!(); + }, + } + } else { + panic!("Unknown type: {}", self.cur_token.literal); + //nodes::Type::Identifier(nodes::Identifier { value: self.cur_token.literal.clone() }) + } + } + fn parse_top_level_statement(&mut self) -> Box { match self.cur_token.kind { lexer::TokenType::Keyword => { @@ -166,23 +189,7 @@ impl Parser { self.next_token(); - let builtin_types = vec!["int"]; - - if self.cur_token.kind != lexer::TokenType::Identifier { - self.error(format!("Expected identifier, found {:#?}", self.cur_token.kind), self.cur_token.line, self.cur_token.pos, self.cur_token.length, Some(1)); - } - - let kind = if builtin_types.contains(&self.cur_token.literal.as_str()) { - match self.cur_token.literal.as_str() { - "int" => nodes::Type::Int, - _ => { - self.error(format!("Unknown type: {}", self.cur_token.literal), self.cur_token.line, self.cur_token.pos, self.cur_token.length, Some(1)); - panic!(); - }, - } - } else { - nodes::Type::Identifier(nodes::Identifier { value: self.cur_token.literal.clone() }) - }; + let kind = self.valid_type(); self.next_token(); @@ -311,23 +318,7 @@ impl Parser { self.next_token(); - let builtin_types = vec!["int"]; - - if self.cur_token.kind != lexer::TokenType::Identifier { - self.error(format!("Expected identifier, found {:#?}", self.cur_token.kind), self.cur_token.line, self.cur_token.pos, self.cur_token.length, Some(1)); - } - - let kind = if builtin_types.contains(&self.cur_token.literal.as_str()) { - match self.cur_token.literal.as_str() { - "int" => nodes::Type::Int, - _ => { - self.error(format!("Unknown type: {}", self.cur_token.literal), self.cur_token.line, self.cur_token.pos, self.cur_token.length, Some(1)); - panic!(); - }, - } - } else { - nodes::Type::Identifier(nodes::Identifier { value: self.cur_token.literal.clone() }) - }; + let kind = self.valid_type(); self.next_token(); @@ -354,23 +345,7 @@ impl Parser { self.next_token(); - let builtin_types = vec!["int"]; - - if self.cur_token.kind != lexer::TokenType::Identifier { - self.error(format!("Expected identifier, found {:#?}", self.cur_token.kind), self.cur_token.line, self.cur_token.pos, self.cur_token.length, Some(1)); - } - - let kind = if builtin_types.contains(&self.cur_token.literal.as_str()) { - match self.cur_token.literal.as_str() { - "int" => nodes::Type::Int, - _ => { - self.error(format!("Unknown type: {}", self.cur_token.literal), self.cur_token.line, self.cur_token.pos, self.cur_token.length, Some(1)); - panic!(); - }, - } - } else { - nodes::Type::Identifier(nodes::Identifier { value: self.cur_token.literal.clone() }) - }; + let kind = self.valid_type(); self.next_token(); @@ -496,7 +471,7 @@ impl Parser { if self.is_assignment() { let ident = match *node { - nodes::Expression::Var(ref ident) => { + nodes::Expression::Var(ref ident, _) => { ident.clone() }, _ => { @@ -516,38 +491,46 @@ impl Parser { parsed_expr }, lexer::TokenType::AddAssign => { - Box::new(nodes::Expression::BinOp(node.clone(), nodes::BinOp::Add, parsed_expr)) + Box::new(nodes::Expression::BinOp(node.clone(), nodes::BinOp::Add, parsed_expr, None)) }, lexer::TokenType::SubtractAssign => { - Box::new(nodes::Expression::BinOp(node.clone(), nodes::BinOp::Subtract, parsed_expr)) + Box::new(nodes::Expression::BinOp(node.clone(), nodes::BinOp::Subtract, parsed_expr, None)) }, lexer::TokenType::MultiplyAssign => { - Box::new(nodes::Expression::BinOp(node.clone(), nodes::BinOp::Multiply, parsed_expr)) + Box::new(nodes::Expression::BinOp(node.clone(), nodes::BinOp::Multiply, parsed_expr, None)) }, lexer::TokenType::DivideAssign => { - Box::new(nodes::Expression::BinOp(node.clone(), nodes::BinOp::Divide, parsed_expr)) + Box::new(nodes::Expression::BinOp(node.clone(), nodes::BinOp::Divide, parsed_expr, None)) }, _ => unreachable!(), }; - node = Box::new(nodes::Expression::Assignment(ident, right)); + node = Box::new(nodes::Expression::Assignment(ident, right, None)); } else { let operator = self.parse_binop(); self.next_token(); let right = self.parse_expression(prec + 1); - node = Box::new(nodes::Expression::BinOp(node, operator, right)); + node = Box::new(nodes::Expression::BinOp(node, operator, right, None)); } } node } + fn parse_int_literal(&mut self) -> Box { + let int = self.cur_token.literal.parse::().unwrap(); + self.next_token(); + if int > i32::MAX as i64 { + Box::new(nodes::Expression::Literal(nodes::Literal::I64(int), None)) + } else { + Box::new(nodes::Expression::Literal(nodes::Literal::I32(int as i32), None)) + } + } + fn parse_factor(&mut self) -> Box { match self.cur_token.kind { lexer::TokenType::Int => { - let int = self.cur_token.literal.parse::().unwrap(); - self.next_token(); - Box::new(nodes::Expression::Literal(nodes::Literal::Int(int))) + self.parse_int_literal() }, lexer::TokenType::Identifier => { self.parse_identifier() @@ -557,7 +540,7 @@ impl Parser { let op = self.parse_unop(); self.next_token(); let expr = self.parse_factor(); - Box::new(nodes::Expression::UnaryOp(op, expr)) + Box::new(nodes::Expression::UnaryOp(op, expr, None)) }, lexer::TokenType::LParen => { self.next_token(); @@ -572,7 +555,7 @@ impl Parser { lexer::TokenType::Pointer => { self.next_token(); let right = self.parse_factor(); - Box::new(nodes::Expression::UnaryOp(nodes::UnaryOp::Reference, right)) + Box::new(nodes::Expression::UnaryOp(nodes::UnaryOp::Reference, right, None)) } _ => { self.error(format!("Expected factor, found {:#?}", self.cur_token.kind), self.cur_token.line, self.cur_token.pos, self.cur_token.length, Some(1)); @@ -588,7 +571,7 @@ impl Parser { if self.cur_token.kind != lexer::TokenType::LParen { return Box::new(nodes::Expression::Var(nodes::Identifier { value: ident, - })); + }, None)); } self.next_token(); @@ -612,7 +595,7 @@ impl Parser { self.next_token(); - Box::new(nodes::Expression::FunctionCall(ident, args)) + Box::new(nodes::Expression::FunctionCall(ident, args, None)) } @@ -652,7 +635,7 @@ mod tests { nodes::Statement::FunctionDeclaration(ref f) => { assert_eq!(f.function_name, "main"); assert_eq!(f.params.len(), 0); - assert_eq!(f.return_type, nodes::Type::Int); + assert_eq!(f.return_type, nodes::Type::I32); }, _ => panic!("expected function declaration"), } @@ -674,7 +657,7 @@ mod tests { nodes::Statement::FunctionDeclaration(ref f) => { assert_eq!(f.function_name, "main"); assert_eq!(f.params.len(), 2); - assert_eq!(f.return_type, nodes::Type::Int); + assert_eq!(f.return_type, nodes::Type::I32); }, _ => panic!("expected function declaration"), } @@ -700,15 +683,16 @@ mod tests { assert_eq!(c.statements.len(), 2); assert_eq!(c.statements[0], Box::new(nodes::Statement::IfStatement(nodes::IfStatement { condition: Box::new(nodes::Expression::BinOp( - Box::new(nodes::Expression::Literal(nodes::Literal::Int(1))), + Box::new(nodes::Expression::Literal(nodes::Literal::I32(1), None)), nodes::BinOp::Equal, - Box::new(nodes::Expression::Literal(nodes::Literal::Int(1))) + Box::new(nodes::Expression::Literal(nodes::Literal::I32(1), None)), + None )), consequence: Box::new(nodes::Statement::Compound(nodes::CompoundStatement { statements: vec![] })), alternative: None, }))); assert_eq!(c.statements[1], Box::new(nodes::Statement::ReturnStatement(nodes::ReturnStatement { - return_value: Box::new(nodes::Expression::Literal(nodes::Literal::Int(6))) + return_value: Box::new(nodes::Expression::Literal(nodes::Literal::I32(6), None)) }))); }, _ => panic!("expected compound statement"), diff --git a/src/parser/nodes.rs b/src/parser/nodes.rs index 0af96d2..22221e5 100644 --- a/src/parser/nodes.rs +++ b/src/parser/nodes.rs @@ -72,18 +72,19 @@ pub struct CompoundStatement { // allow debug clone and equal (compare? wtv its called) #[derive(Debug, Clone, PartialEq)] pub enum Expression { - Literal(Literal), - Var(Identifier), - BinOp(Box, BinOp, Box), - UnaryOp(UnaryOp, Box), - Assignment(Identifier, Box), - FunctionCall(String, Vec>), + Literal(Literal, Option), + Var(Identifier, Option), + BinOp(Box, BinOp, Box, Option), + UnaryOp(UnaryOp, Box, Option), + Assignment(Identifier, Box, Option), + FunctionCall(String, Vec>, Option), + Cast(Box, Option), } #[derive(Debug, Clone, Copy, PartialEq)] pub enum Literal { - Int(i32), - Bool(bool), + I32(i32), + I64(i64), } #[derive(Debug, Clone, PartialEq)] @@ -117,9 +118,10 @@ pub enum UnaryOp { #[derive(Debug, Clone, PartialEq)] pub enum Type { - Int, + I32, + I64, Fn(Vec, Box), - Identifier(Identifier) + //Identifier(Identifier) } // TODO: improve line stuff for errors \ No newline at end of file diff --git a/src/semantic_analysis.rs b/src/semantic_analysis.rs index 9e8df88..6997812 100644 --- a/src/semantic_analysis.rs +++ b/src/semantic_analysis.rs @@ -16,7 +16,7 @@ impl Analysis { } } - pub fn run(&mut self) -> parser::nodes::Program { + pub fn run(&mut self) -> (parser::nodes::Program, symbol_table::SymbolTable) { let first_pass = identifier_resolution_pass::Pass::new(&self.ast); let program = first_pass.run(); @@ -27,8 +27,8 @@ impl Analysis { let third_pass = type_checking_pass::Pass::new(&program); - let sym_tbl = third_pass.run(); + let (program, sym_tbl) = third_pass.run(); - program + (program, sym_tbl) } } \ No newline at end of file diff --git a/src/semantic_analysis/identifier_resolution_pass.rs b/src/semantic_analysis/identifier_resolution_pass.rs index 571e39f..a1f74ff 100644 --- a/src/semantic_analysis/identifier_resolution_pass.rs +++ b/src/semantic_analysis/identifier_resolution_pass.rs @@ -159,27 +159,27 @@ impl Pass { fn resolve_expression(&self, expr: Box, context: &mut Context) -> Box { match *expr { - nodes::Expression::Var(ref ident) => { + nodes::Expression::Var(ref ident, _) => { if context.identifier_map.map.contains_key(&ident.value) { - Box::new(nodes::Expression::Var(nodes::Identifier { value: context.identifier_map.map.get(&ident.value).unwrap().clone().0 })) + Box::new(nodes::Expression::Var(nodes::Identifier { value: context.identifier_map.map.get(&ident.value).unwrap().clone().0 }, None)) } else { panic!("Variable {} not found in scope", ident.value); } }, - nodes::Expression::Assignment(ref ident, ref expr) => { + nodes::Expression::Assignment(ref ident, ref expr, _) => { if context.identifier_map.map.contains_key(&ident.value) { - Box::new(nodes::Expression::Assignment(nodes::Identifier { value: context.identifier_map.map.get(&ident.value).unwrap().clone().0 }, self.resolve_expression(expr.clone(), context))) + Box::new(nodes::Expression::Assignment(nodes::Identifier { value: context.identifier_map.map.get(&ident.value).unwrap().clone().0 }, self.resolve_expression(expr.clone(), context), None)) } else { panic!("Variable {} not found in scope", ident.value); } }, - nodes::Expression::BinOp(ref left, ref op, ref right) => { - Box::new(nodes::Expression::BinOp(self.resolve_expression(left.clone(), context), op.clone(), self.resolve_expression(right.clone(), context))) + nodes::Expression::BinOp(ref left, ref op, ref right, _) => { + Box::new(nodes::Expression::BinOp(self.resolve_expression(left.clone(), context), op.clone(), self.resolve_expression(right.clone(), context), None)) }, - nodes::Expression::UnaryOp(ref op, ref expr) => { - Box::new(nodes::Expression::UnaryOp(op.clone(), self.resolve_expression(expr.clone(), context))) + nodes::Expression::UnaryOp(ref op, ref expr, _) => { + Box::new(nodes::Expression::UnaryOp(op.clone(), self.resolve_expression(expr.clone(), context), None)) }, - nodes::Expression::FunctionCall(ref name, ref args) => { + nodes::Expression::FunctionCall(ref name, ref args, _) => { let calling_name = if !context.identifier_map.map.contains_key(name) { //panic!("Function {} not found in scope", name); // for now keep this commented out, it allows for the usage of c libraries before we've implemented imports name.clone() @@ -193,7 +193,10 @@ impl Pass { new_args.push(self.resolve_expression(arg.clone(), context)); } - Box::new(nodes::Expression::FunctionCall(calling_name, new_args)) + Box::new(nodes::Expression::FunctionCall(calling_name, new_args, None)) + }, + nodes::Expression::Cast(ref expr, _) => { + Box::new(nodes::Expression::Cast(self.resolve_expression(expr.clone(), context), None)) }, _ => expr, } @@ -212,17 +215,17 @@ mod tests { function_definitions: vec![ nodes::FunctionDeclaration { function_name: "main".to_string(), - return_type: nodes::Type::Int, + return_type: nodes::Type::I32, params: vec![], body: Box::new(nodes::Statement::Compound(nodes::CompoundStatement { statements: vec![ Box::new(nodes::Statement::VariableDeclaration(nodes::VariableDeclaration { - kind: nodes::Type::Int, + kind: nodes::Type::I32, ident: nodes::Identifier { value: "a".to_string() }, - expr: Some(Box::new(nodes::Expression::Literal(nodes::Literal::Int(5)))), + expr: Some(Box::new(nodes::Expression::Literal(nodes::Literal::I32(5), None))), })), Box::new(nodes::Statement::ExpressionStatement(nodes::ExpressionStatement { - expression: Box::new(nodes::Expression::Var(nodes::Identifier { value: "a".to_string() })), + expression: Box::new(nodes::Expression::Var(nodes::Identifier { value: "a".to_string() }, None)), })), ], })), @@ -236,12 +239,12 @@ mod tests { assert_eq!(new_program.function_definitions[0].body, Box::new(nodes::Statement::Compound(nodes::CompoundStatement { statements: vec![ Box::new(nodes::Statement::VariableDeclaration(nodes::VariableDeclaration { - kind: nodes::Type::Int, + kind: nodes::Type::I32, ident: nodes::Identifier { value: ".localvar0".to_string() }, - expr: Some(Box::new(nodes::Expression::Literal(nodes::Literal::Int(5)))), + expr: Some(Box::new(nodes::Expression::Literal(nodes::Literal::I32(5), None))), })), Box::new(nodes::Statement::ExpressionStatement(nodes::ExpressionStatement { - expression: Box::new(nodes::Expression::Var(nodes::Identifier { value: ".localvar0".to_string() })), + expression: Box::new(nodes::Expression::Var(nodes::Identifier { value: ".localvar0".to_string() }, None)), })), ], }))); @@ -254,19 +257,19 @@ mod tests { function_definitions: vec![ nodes::FunctionDeclaration { function_name: "main".to_string(), - return_type: nodes::Type::Int, + return_type: nodes::Type::I32, params: vec![], body: Box::new(nodes::Statement::Compound(nodes::CompoundStatement { statements: vec![ Box::new(nodes::Statement::VariableDeclaration(nodes::VariableDeclaration { - kind: nodes::Type::Int, + kind: nodes::Type::I32, ident: nodes::Identifier { value: "a".to_string() }, - expr: Some(Box::new(nodes::Expression::Literal(nodes::Literal::Int(5)))), + expr: Some(Box::new(nodes::Expression::Literal(nodes::Literal::I32(5), None))), })), Box::new(nodes::Statement::VariableDeclaration(nodes::VariableDeclaration { - kind: nodes::Type::Int, + kind: nodes::Type::I32, ident: nodes::Identifier { value: "a".to_string() }, - expr: Some(Box::new(nodes::Expression::Literal(nodes::Literal::Int(5)))), + expr: Some(Box::new(nodes::Expression::Literal(nodes::Literal::I32(5), None))), })), ], })), @@ -284,17 +287,17 @@ mod tests { function_definitions: vec![ nodes::FunctionDeclaration { function_name: "main".to_string(), - return_type: nodes::Type::Int, + return_type: nodes::Type::I32, params: vec![], body: Box::new(nodes::Statement::Compound(nodes::CompoundStatement { statements: vec![ Box::new(nodes::Statement::VariableDeclaration(nodes::VariableDeclaration { - kind: nodes::Type::Int, + kind: nodes::Type::I32, ident: nodes::Identifier { value: "a".to_string() }, - expr: Some(Box::new(nodes::Expression::Literal(nodes::Literal::Int(5)))), + expr: Some(Box::new(nodes::Expression::Literal(nodes::Literal::I32(5), None))), })), Box::new(nodes::Statement::ExpressionStatement(nodes::ExpressionStatement { - expression: Box::new(nodes::Expression::Assignment(nodes::Identifier { value: "a".to_string() }, Box::new(nodes::Expression::Literal(nodes::Literal::Int(10)) ))), + expression: Box::new(nodes::Expression::Assignment(nodes::Identifier { value: "a".to_string() }, Box::new(nodes::Expression::Literal(nodes::Literal::I32(10), None) ), None)), })), ], })), @@ -308,12 +311,12 @@ mod tests { assert_eq!(new_program.function_definitions[0].body, Box::new(nodes::Statement::Compound(nodes::CompoundStatement { statements: vec![ Box::new(nodes::Statement::VariableDeclaration(nodes::VariableDeclaration { - kind: nodes::Type::Int, + kind: nodes::Type::I32, ident: nodes::Identifier { value: ".localvar0".to_string() }, - expr: Some(Box::new(nodes::Expression::Literal(nodes::Literal::Int(5)))), + expr: Some(Box::new(nodes::Expression::Literal(nodes::Literal::I32(5), None))), })), Box::new(nodes::Statement::ExpressionStatement(nodes::ExpressionStatement { - expression: Box::new(nodes::Expression::Assignment(nodes::Identifier { value: ".localvar0".to_string() }, Box::new(nodes::Expression::Literal(nodes::Literal::Int(10)) ))), + expression: Box::new(nodes::Expression::Assignment(nodes::Identifier { value: ".localvar0".to_string() }, Box::new(nodes::Expression::Literal(nodes::Literal::I32(10), None) ), None)), })), ], }))); @@ -326,12 +329,12 @@ mod tests { function_definitions: vec![ nodes::FunctionDeclaration { function_name: "main".to_string(), - return_type: nodes::Type::Int, + return_type: nodes::Type::I32, params: vec![], body: Box::new(nodes::Statement::Compound(nodes::CompoundStatement { statements: vec![ Box::new(nodes::Statement::ExpressionStatement(nodes::ExpressionStatement { - expression: Box::new(nodes::Expression::Assignment(nodes::Identifier { value: "a".to_string() }, Box::new(nodes::Expression::Literal(nodes::Literal::Int(10)) ))), + expression: Box::new(nodes::Expression::Assignment(nodes::Identifier { value: "a".to_string() }, Box::new(nodes::Expression::Literal(nodes::Literal::I32(10), None) ), None)), })), ], })), diff --git a/src/semantic_analysis/symbol_table.rs b/src/semantic_analysis/symbol_table.rs index 915f228..c51f3d8 100644 --- a/src/semantic_analysis/symbol_table.rs +++ b/src/semantic_analysis/symbol_table.rs @@ -1,6 +1,8 @@ #![allow(dead_code)] use std::collections::HashMap; +use crate::parser::nodes::Type; + #[derive(Debug, Clone, PartialEq)] pub struct SymbolTable { @@ -21,11 +23,4 @@ impl SymbolTable { pub fn get(&self, key: &str) -> Option<&Type> { self.table.get(key) } -} - -#[derive(Debug, Clone, PartialEq)] -pub enum Type { - Int, - Fn(Vec, Box), - Identifier(String), } \ No newline at end of file diff --git a/src/semantic_analysis/type_checking_pass.rs b/src/semantic_analysis/type_checking_pass.rs index e6288ac..398dc81 100644 --- a/src/semantic_analysis/type_checking_pass.rs +++ b/src/semantic_analysis/type_checking_pass.rs @@ -5,6 +5,10 @@ pub struct Pass { pub program: nodes::Program, } +struct Context { + pub func_return_type: nodes::Type, +} + impl Pass { pub fn new(program: &nodes::Program) -> Self { Self { @@ -12,19 +16,27 @@ impl Pass { } } - pub fn run(&self) -> symbol_table::SymbolTable { + pub fn run(&self) -> (nodes::Program, symbol_table::SymbolTable) { let mut symbol_table = symbol_table::SymbolTable::new(); // put all the functions in there for function in &self.program.function_definitions { - symbol_table.insert(function.function_name.clone(), symbol_table::Type::Fn( + symbol_table.insert(function.function_name.clone(), nodes::Type::Fn( function.params.iter().map(|arg| self.convert_type(&arg.kind)).collect(), Box::new(self.convert_type(&function.return_type)) )); } + let mut program = nodes::Program { + function_definitions: Vec::new(), + }; + // now go over the functions contents and add the variables for function in &self.program.function_definitions { + let context = Context { + func_return_type: function.return_type.clone(), + }; + for arg in &function.params { symbol_table.insert(arg.ident.value.clone(), self.convert_type(&arg.kind)); } @@ -34,136 +46,225 @@ impl Pass { _ => unreachable!(), }; + let mut new_body: Vec> = Vec::new(); + for statement in body { - self.typecheck_statement(statement, &mut symbol_table); + new_body.push(self.typecheck_statement(statement, &mut symbol_table, &context)); } + + program.function_definitions.push(nodes::FunctionDeclaration { + function_name: function.function_name.clone(), + params: function.params.clone(), + return_type: function.return_type.clone(), + body: Box::new(nodes::Statement::Compound(nodes::CompoundStatement { + statements: new_body, + })), + }); } - symbol_table + (program, symbol_table) + } + + fn convert_to(&self, e: nodes::Expression, t: nodes::Type, symbol_table: &symbol_table::SymbolTable) -> Box { + let typed_e = self.typecheck_expression(Box::new(e), symbol_table); + + if typed_e.1 == t { + typed_e.0 + } else { + Box::new(nodes::Expression::Cast(typed_e.0, Some(t))) + } + } + + fn get_common_type(&self, left: nodes::Type, right: nodes::Type) -> nodes::Type { + if left == right { + left + } else { + nodes::Type::I64 + } } - fn typecheck_statement(&self, statement: Box, symbol_table: &mut symbol_table::SymbolTable) { + fn typecheck_statement(&self, statement: Box, symbol_table: &mut symbol_table::SymbolTable, context: &Context) -> Box { match *statement { nodes::Statement::VariableDeclaration(decl) => { let type_ = self.convert_type(&decl.kind); - let ident = decl.ident.value; + let ident = decl.ident.value.clone(); - let expr_type = match decl.expr { + let typed_expr = match decl.expr { Some(expr) => self.typecheck_expression(expr, symbol_table), - None => type_.clone(), + None => (Box::new(nodes::Expression::Literal(nodes::Literal::I32(0), Some(nodes::Type::I32))), nodes::Type::I32), }; + let converted_expr = self.convert_to(*typed_expr.0.clone(), type_.clone(), symbol_table); + if symbol_table.get(&ident).is_some() { panic!("Variable {} already declared", ident); } - symbol_table.insert(ident, type_); + symbol_table.insert(ident, type_.clone()); + + Box::new(nodes::Statement::VariableDeclaration(nodes::VariableDeclaration { + kind: type_, + ident: decl.ident, + expr: Some(converted_expr), + })) }, nodes::Statement::ReturnStatement(ret) => { let type_ = self.typecheck_expression(ret.return_value, symbol_table); - // todo: check if the return type is the same as the outer function return type - if type_ != symbol_table::Type::Int { - panic!("Return type is not int"); + if type_.1 != context.func_return_type { + panic!("Return type mismatch: value {:?} and fn {:?}", type_.1, context.func_return_type); } + + Box::new(nodes::Statement::ReturnStatement(nodes::ReturnStatement { + return_value: type_.0, + })) }, nodes::Statement::ExpressionStatement(expr) => { - self.typecheck_expression(expr.expression, symbol_table); + let expr = self.typecheck_expression(expr.expression, symbol_table).0; + + Box::new(nodes::Statement::ExpressionStatement(nodes::ExpressionStatement { + expression: expr, + })) }, nodes::Statement::IfStatement(if_) => { - self.typecheck_expression(if_.condition, symbol_table); - self.typecheck_statement(if_.consequence, symbol_table); + let cond = self.typecheck_expression(if_.condition, symbol_table).0; + let cnsq = self.typecheck_statement(if_.consequence, symbol_table, context); - if let Some(alt) = if_.alternative { - self.typecheck_statement(alt, symbol_table); - } + let alt = if let Some(alt) = if_.alternative { + Some(self.typecheck_statement(alt, symbol_table, context)) + } else { + None + }; + + Box::new(nodes::Statement::IfStatement(nodes::IfStatement { + condition: cond, + consequence: cnsq, + alternative: alt, + })) }, nodes::Statement::WhileStatement(while_) => { - self.typecheck_expression(while_.condition, symbol_table); - self.typecheck_statement(while_.body, symbol_table); + let cond = self.typecheck_expression(while_.condition, symbol_table).0; + let body = self.typecheck_statement(while_.body, symbol_table, context); + + Box::new(nodes::Statement::WhileStatement(nodes::WhileStatement { + condition: cond, + body: body, + label: while_.label.clone(), + })) }, nodes::Statement::Compound(compound) => { + let mut new_statements = Vec::new(); + for statement in compound.statements { - self.typecheck_statement(statement, symbol_table); + new_statements.push(self.typecheck_statement(statement, symbol_table, context)); } + + Box::new(nodes::Statement::Compound(nodes::CompoundStatement { + statements: new_statements, + })) }, _ => unimplemented!() } } - fn typecheck_expression(&self, expression: Box, symbol_table: &symbol_table::SymbolTable) -> symbol_table::Type { + fn typecheck_expression(&self, expression: Box, symbol_table: &symbol_table::SymbolTable) -> (Box, nodes::Type) { match *expression { - nodes::Expression::Assignment(ident, expr) => { - let type_ = self.typecheck_expression(expr, symbol_table); - - if symbol_table.get(&ident.value).is_none() { + nodes::Expression::Assignment(ident, expr, _) => { + let type_ = if let Some(type_) = symbol_table.get(&ident.value) { + type_.clone() + } else { panic!("Variable {} not declared", ident.value); - } + }; - if symbol_table.get(&ident.value).unwrap() != &type_ { - panic!("Variable {} declared as {:?}, but assigned as {:?}", ident.value, symbol_table.get(&ident.value).unwrap(), type_); - } else { - type_ - } + let typed_expr = self.typecheck_expression(expr, symbol_table); + + let converted_expr = self.convert_to(*typed_expr.0.clone(), typed_expr.1.clone(), symbol_table); + + (Box::new(nodes::Expression::Assignment(ident, converted_expr, Some(type_.clone()))), type_) }, - nodes::Expression::BinOp(left, _, right) => { - let left_type = self.typecheck_expression(left, symbol_table); - let right_type = self.typecheck_expression(right, symbol_table); + nodes::Expression::BinOp(left, op, right, _) => { + let typed_left = self.typecheck_expression(left, symbol_table); + let typed_right = self.typecheck_expression(right, symbol_table); - if left_type != right_type { - panic!("Type mismatch in binop: {:?} and {:?}", left_type, right_type); - } else { - left_type + if op == nodes::BinOp::And || op == nodes::BinOp::Or { + return (Box::new(nodes::Expression::BinOp(typed_left.0, op, typed_right.0, Some(nodes::Type::I32))), nodes::Type::I32); } + + let common_type = self.get_common_type(typed_left.1.clone(), typed_right.1.clone()); + + let converted_left = self.convert_to(*typed_left.0.clone(), common_type.clone(), symbol_table); + let converted_right = self.convert_to(*typed_right.0.clone(), common_type.clone(), symbol_table); + + (Box::new(nodes::Expression::BinOp(converted_left, op, converted_right, Some(common_type.clone()))), common_type) }, - nodes::Expression::UnaryOp(_, expr) => { - self.typecheck_expression(expr, symbol_table) + nodes::Expression::UnaryOp(op, expr, _) => { + let typed_expr = self.typecheck_expression(expr, symbol_table); + + (Box::new(nodes::Expression::UnaryOp(op, typed_expr.0, Some(typed_expr.1.clone()))), typed_expr.1) }, - nodes::Expression::Var(ident) => { + nodes::Expression::Var(ident, _) => { if let Some(type_) = symbol_table.get(&ident.value) { - type_.clone() + (Box::new(nodes::Expression::Var(ident, Some(type_.clone()))), type_.clone()) } else { panic!("Variable {} not declared", ident.value); } }, - nodes::Expression::Literal(literal) => { + nodes::Expression::Literal(literal, _) => { match literal { - nodes::Literal::Int(_) => symbol_table::Type::Int, + nodes::Literal::I32(val) => (Box::new(nodes::Expression::Literal(nodes::Literal::I32(val), Some(nodes::Type::I32))), nodes::Type::I32), + nodes::Literal::I64(val) => (Box::new(nodes::Expression::Literal(nodes::Literal::I64(val), Some(nodes::Type::I64))), nodes::Type::I64), + #[allow(unreachable_patterns)] _ => unimplemented!() } }, - nodes::Expression::FunctionCall(name, args) => { + nodes::Expression::FunctionCall(name, args, _) => { let op_func_type = symbol_table.get(&name); let function_type = match op_func_type { Some(_) => op_func_type.unwrap().clone(), - _ => symbol_table::Type::Int //panic!("Function {} not declared", name), + _ => nodes::Type::I32 //panic!("Function {} not declared", name), }; - let arg_types = args.iter().map(|arg| self.typecheck_expression(arg.clone(), symbol_table)).collect::>(); - - let ret_type = if let symbol_table::Type::Fn(ref params, ret) = function_type { - if params != &arg_types { - panic!("Function {} called with wrong arguments", name); - } - ret + let (params, ret_type) = if let nodes::Type::Fn(ref params, ret_type) = function_type { + (params, *ret_type) } else { - //panic!("Variable {} is not a function", name); - Box::new(symbol_table::Type::Int) + panic!("Variable {} is not a function", name); }; - *ret_type + let mut typed_args: Vec<(Box, nodes::Type)> = Vec::new(); + + if args.len() != params.len() { + panic!("Function {} expects {} arguments, got {}", name, params.len(), args.len()); + } + + for (i, arg) in args.iter().enumerate() { + let typed_arg = self.typecheck_expression(arg.clone(), symbol_table); + + let converted_arg = self.convert_to(*typed_arg.0.clone(), params[i].clone(), symbol_table); + + typed_args.push((converted_arg, typed_arg.1)); + } + + let arg_types = typed_args.iter().map(|arg| arg.1.clone()).collect::>(); + + (Box::new(nodes::Expression::FunctionCall(name, typed_args.iter().map(|arg| arg.0.clone()).collect(), Some(ret_type.clone()))), ret_type.clone()) + }, + nodes::Expression::Cast(expr, _) => { + let typed_expr = self.typecheck_expression(expr.clone(), symbol_table); + + (Box::new(nodes::Expression::Cast(expr, Some(typed_expr.1.clone()))), typed_expr.1) }, #[allow(unreachable_patterns)] _ => unimplemented!() } } - fn convert_type(&self, type_: &nodes::Type) -> symbol_table::Type { + fn convert_type(&self, type_: &nodes::Type) -> nodes::Type { match type_ { - nodes::Type::Int => symbol_table::Type::Int, - nodes::Type::Identifier(ident) => symbol_table::Type::Identifier(ident.value.clone()), - nodes::Type::Fn(params, return_type) => symbol_table::Type::Fn( + nodes::Type::I32 => nodes::Type::I32, + nodes::Type::I64 => nodes::Type::I64, + //nodes::Type::Identifier(ident) => typed_ast::Type::Identifier(ident.value.clone()), + nodes::Type::Fn(params, return_type) => nodes::Type::Fn( params.iter().map(|arg| self.convert_type(arg)).collect(), Box::new(self.convert_type(return_type)) ), diff --git a/src/tacky.rs b/src/tacky.rs index 7a89c22..657b491 100644 --- a/src/tacky.rs +++ b/src/tacky.rs @@ -11,6 +11,7 @@ pub struct Context { pub struct Tacky { pub ast: parser::nodes::Program, + pub symbol_table: nodes::SymbolTable, pub context: Context, } @@ -22,14 +23,27 @@ impl Tacky { tmp_n: -1, label_n: -1, }, + symbol_table: nodes::SymbolTable::new() } } - fn make_temporary(&mut self) -> String { + fn _make_temporary(&mut self) -> String { self.context.tmp_n += 1; format!(".tmp{}", self.context.tmp_n) } + fn make_var(&mut self, name: String, type_: nodes::Type) -> nodes::Value { + self.symbol_table.insert(name.clone(), type_); + + nodes::Value::Identifier(name) + } + + fn make_tacky_var(&mut self, type_: nodes::Type) -> nodes::Value { + let name = self._make_temporary(); + + self.make_var(name, type_) + } + fn make_label(&mut self) -> String { self.context.label_n += 1; format!(".L{}", self.context.label_n) @@ -37,7 +51,8 @@ impl Tacky { fn convert_type(&self, ty: &parser::nodes::Type) -> nodes::Type { match ty { - parser::nodes::Type::Int => nodes::Type::Int, + parser::nodes::Type::I32 => nodes::Type::I32, + parser::nodes::Type::I64 => nodes::Type::I64, parser::nodes::Type::Fn(ref args, ref ret) => { let mut arg_types = Vec::new(); for arg in args { @@ -45,7 +60,11 @@ impl Tacky { } nodes::Type::Fn(arg_types, Box::new(self.convert_type(ret))) } - parser::nodes::Type::Identifier(ident) => nodes::Type::Identifier(ident.value.clone()), + #[allow(unreachable_patterns)] + _ => { + panic!("Not implemented yet: {:?}", ty); + } + // parser::nodes::Type::Identifier(ident) => nodes::Type::Identifier(ident.value.clone()), } } @@ -62,8 +81,13 @@ impl Tacky { let ret_type = self.convert_type(&statement.return_type); + // add the args to the symbol table + for arg in statement.params.iter() { + self.make_var(arg.ident.value.clone(), self.convert_type(&arg.kind)); + } + self.emit_tacky_statement(&*statement.body, &mut instructions); - instructions.instructions.push(nodes::Instruction::Return(nodes::Value::Constant(0))); + instructions.instructions.push(nodes::Instruction::Return(nodes::Value::Constant(nodes::Constant::I32(0)))); program.function_definitions.push(nodes::FunctionDefinition { function_name: statement.function_name.clone(), body: instructions, @@ -96,10 +120,10 @@ impl Tacky { self.emit_tacky_expression(&*expression.expression, instructions); } parser::nodes::Statement::VariableDeclaration(ref decl) => { - let var = nodes::Value::Identifier(decl.ident.value.clone()); + let var = self.make_var(decl.ident.value.clone(), self.convert_type(&decl.kind)); let value = match &decl.expr { Some(value) => self.emit_tacky_expression(&*value, instructions), - None => nodes::Value::Constant(0), + None => nodes::Value::Constant(nodes::Constant::I32(0)) }; instructions.instructions.push(nodes::Instruction::Copy(nodes::Copy { src: value, @@ -107,16 +131,16 @@ impl Tacky { })); } parser::nodes::Statement::IfStatement(ref if_statement) => { - let cond = self.make_temporary(); + let cond = self.make_tacky_var(self.get_tacky_type(&if_statement.condition)); let else_label = self.make_label(); let condition = self.emit_tacky_expression(&*if_statement.condition, instructions); instructions.instructions.push(nodes::Instruction::Copy(nodes::Copy { src: condition, - dest: nodes::Value::Identifier(cond.clone()), + dest: cond.clone(), })); instructions.instructions.push(nodes::Instruction::JumpIfZero( else_label.clone(), - nodes::Value::Identifier(cond), + cond, )); self.emit_tacky_statement(&*if_statement.consequence, instructions); if if_statement.alternative.is_some() { @@ -133,15 +157,15 @@ impl Tacky { let start_label = format!(".L_continue_{}", while_statement.label); let end_label = format!(".L_break_{}", while_statement.label); instructions.instructions.push(nodes::Instruction::Label(start_label.clone())); - let cond = self.make_temporary(); + let cond = self.make_tacky_var(self.get_tacky_type(&while_statement.condition)); let condition = self.emit_tacky_expression(&*while_statement.condition, instructions); instructions.instructions.push(nodes::Instruction::Copy(nodes::Copy { src: condition, - dest: nodes::Value::Identifier(cond.clone()), + dest: cond.clone(), })); instructions.instructions.push(nodes::Instruction::JumpIfZero( end_label.clone(), - nodes::Value::Identifier(cond), + cond, )); self.emit_tacky_statement(&*while_statement.body, instructions); instructions.instructions.push(nodes::Instruction::Jump(start_label)); @@ -157,17 +181,78 @@ impl Tacky { }; } + fn get_tacky_type(&self, expression: &parser::nodes::Expression) -> nodes::Type { + self.convert_type(&self.get_parser_type(expression)) + } + + fn get_parser_type(&self, expression: &parser::nodes::Expression) -> parser::nodes::Type { + match expression { + parser::nodes::Expression::Literal(_, type_) => { + if type_.is_none() { + panic!("Type not specified for literal"); + } + type_.clone().unwrap() + }, + parser::nodes::Expression::UnaryOp(_, exp, type_) => { + if type_.is_none() { + panic!("Type not specified for unary expression"); + } + type_.clone().unwrap() + }, + parser::nodes::Expression::BinOp(_, _, _, type_) => { + if type_.is_none() { + panic!("Type not specified for binary expression"); + } + type_.clone().unwrap() + }, + parser::nodes::Expression::Var(_, type_) => { + if type_.is_none() { + panic!("Type not specified for variable"); + } + type_.clone().unwrap() + }, + parser::nodes::Expression::Assignment(_, _, type_) => { + type_.clone().unwrap() + }, + parser::nodes::Expression::FunctionCall(_, _, type_) => { + if type_.is_none() { + panic!("Type not specified for function call"); + } + type_.clone().unwrap() + }, + parser::nodes::Expression::Cast(_, type_) => { + if type_.is_none() { + panic!("Type not specified for cast"); + } + type_.clone().unwrap() + }, + #[allow(unreachable_patterns)] + _ => panic!("Not implemented yet") + } + } + fn emit_tacky_expression(&mut self, expression: &parser::nodes::Expression, instructions: &mut nodes::CompoundInstruction) -> nodes::Value { match expression { - parser::nodes::Expression::Literal(literal) => { + parser::nodes::Expression::Literal(literal, type_) => { + if type_.is_none() { + panic!("Type not specified for literal"); + } match literal { - parser::nodes::Literal::Int(i) => nodes::Value::Constant(*i), + parser::nodes::Literal::I32(i) => nodes::Value::Constant(nodes::Constant::I32(*i)), + parser::nodes::Literal::I64(i) => nodes::Value::Constant(nodes::Constant::I64(*i)), + #[allow(unreachable_patterns)] _ => panic!("Not implemented yet") } }, - parser::nodes::Expression::UnaryOp(op, exp) => { + parser::nodes::Expression::UnaryOp(op, exp, type_) => { + if type_.is_none() { + panic!("Type not specified for unary expression"); + } + + let type_ = self.convert_type(&type_.as_ref().unwrap()); + let src = self.emit_tacky_expression(&*exp, instructions); - let dest = nodes::Value::Identifier(self.make_temporary()); + let dest = self.make_tacky_var(type_); instructions.instructions.push(nodes::Instruction::Unary(nodes::Unary { operator: match op { parser::nodes::UnaryOp::Negation => nodes::UnaryOperator::Negate, @@ -179,7 +264,9 @@ impl Tacky { })); dest }, - parser::nodes::Expression::BinOp(exp1, op, exp2) => { + parser::nodes::Expression::BinOp(exp1, op, exp2, type_) => { + let type_ = self.convert_type(&type_.as_ref().unwrap()); + let operator = match op { parser::nodes::BinOp::Add => nodes::BinaryOperator::Add, parser::nodes::BinOp::Subtract => nodes::BinaryOperator::Subtract, @@ -211,9 +298,9 @@ impl Tacky { } else { instructions.instructions.push(nodes::Instruction::JumpIfNotZero(end.clone(), src1.clone())); } - let dest = nodes::Value::Identifier(self.make_temporary()); + let dest = self.make_tacky_var(nodes::Type::I32); instructions.instructions.push(nodes::Instruction::Copy(nodes::Copy { - src: nodes::Value::Constant(1), + src: nodes::Value::Constant(nodes::Constant::I32(0)), dest: dest.clone(), })); instructions.instructions.push(nodes::Instruction::Label(end)); @@ -221,7 +308,7 @@ impl Tacky { } let src1 = self.emit_tacky_expression(&*exp1, instructions); let src2 = self.emit_tacky_expression(&*exp2, instructions); - let dest = nodes::Value::Identifier(self.make_temporary()); + let dest = self.make_tacky_var(type_); instructions.instructions.push(nodes::Instruction::Binary(nodes::Binary { operator, src1, @@ -230,10 +317,10 @@ impl Tacky { })); dest }, - parser::nodes::Expression::Var(ident) => { + parser::nodes::Expression::Var(ident, type_) => { nodes::Value::Identifier(ident.value.clone()) }, - parser::nodes::Expression::Assignment(ident, exp) => { + parser::nodes::Expression::Assignment(ident, exp, _) => { let value = self.emit_tacky_expression(&*exp, instructions); instructions.instructions.push(nodes::Instruction::Copy(nodes::Copy { src: value.clone(), @@ -241,12 +328,13 @@ impl Tacky { })); value }, - parser::nodes::Expression::FunctionCall(name, args) => { + parser::nodes::Expression::FunctionCall(name, args, type_) => { + let type_ = self.convert_type(&type_.as_ref().unwrap()); let mut arguments = Vec::new(); for arg in args { arguments.push(self.emit_tacky_expression(&*arg, instructions)); } - let dest = nodes::Value::Identifier(self.make_temporary()); + let dest = self.make_tacky_var(type_); instructions.instructions.push(nodes::Instruction::FunCall(nodes::FunCall { function_name: name.clone(), arguments, @@ -254,6 +342,34 @@ impl Tacky { })); dest }, + parser::nodes::Expression::Cast(expr, type_) => { + if type_.is_none() { + panic!("Type not specified for cast"); + } + + let u_type = self.convert_type(&type_.as_ref().unwrap()); + + let value = self.emit_tacky_expression(&*expr, instructions); + + if self.get_tacky_type(&expr) == u_type { + return value; + } + let dest = self.make_tacky_var(u_type.clone()); + + let trun_val = if value.is_constant() { + return nodes::Value::Constant(nodes::Constant::I32(value.as_constant().as_i32())); + } else { + value.clone() + }; + + if u_type == nodes::Type::I64 { + instructions.instructions.push(nodes::Instruction::SignExtend(value, dest.clone())); + } else { + instructions.instructions.push(nodes::Instruction::Truncate(trun_val, dest.clone())); // todo: maybe get rid of this? + } + + dest + }, #[allow(unreachable_patterns)] item => panic!("Not implemented yet: {:?}", item) } @@ -272,11 +388,11 @@ mod tests { function_definitions: vec![ parser::nodes::FunctionDeclaration { function_name: "main".to_string(), - return_type: parser::nodes::Type::Int, + return_type: parser::nodes::Type::I32, body: Box::new(parser::nodes::Statement::Compound(parser::nodes::CompoundStatement { statements: vec![ Box::new(parser::nodes::Statement::ReturnStatement(parser::nodes::ReturnStatement { - return_value: Box::new(parser::nodes::Expression::Literal(parser::nodes::Literal::Int(0))), + return_value: Box::new(parser::nodes::Expression::Literal(parser::nodes::Literal::I32(0), Some(parser::nodes::Type::I32))), })), ], })), @@ -288,10 +404,10 @@ mod tests { let program = tacky.generate(); assert_eq!(program.function_definitions.len(), 1); assert_eq!(program.function_definitions[0].function_name, "main"); - assert_eq!(program.function_definitions[0].return_type, nodes::Type::Int); + assert_eq!(program.function_definitions[0].return_type, nodes::Type::I32); assert_eq!(program.function_definitions[0].body.instructions.len(), 2); - assert_eq!(program.function_definitions[0].body.instructions[0], nodes::Instruction::Return(nodes::Value::Constant(0))); - assert_eq!(program.function_definitions[0].body.instructions[1], nodes::Instruction::Return(nodes::Value::Constant(0))); // Return 0 is added by the generator so if theres no return statement it doesnt bug out + assert_eq!(program.function_definitions[0].body.instructions[0], nodes::Instruction::Return(nodes::Value::Constant(nodes::Constant::I32(0)))); + assert_eq!(program.function_definitions[0].body.instructions[1], nodes::Instruction::Return(nodes::Value::Constant(nodes::Constant::I32(0)))); // Return 0 is added by the generator so if theres no return statement it doesnt bug out } #[test] @@ -300,16 +416,16 @@ mod tests { function_definitions: vec![ parser::nodes::FunctionDeclaration { function_name: "main".to_string(), - return_type: parser::nodes::Type::Int, + return_type: parser::nodes::Type::I32, body: Box::new(parser::nodes::Statement::Compound(parser::nodes::CompoundStatement { statements: vec![ Box::new(parser::nodes::Statement::VariableDeclaration(parser::nodes::VariableDeclaration { ident: parser::nodes::Identifier { value: "x".to_string() }, - expr: Some(Box::new(parser::nodes::Expression::Literal(parser::nodes::Literal::Int(42)))), - kind: parser::nodes::Type::Int, + expr: Some(Box::new(parser::nodes::Expression::Literal(parser::nodes::Literal::I32(42), Some(parser::nodes::Type::I32)))), + kind: parser::nodes::Type::I32, })), Box::new(parser::nodes::Statement::ReturnStatement(parser::nodes::ReturnStatement { - return_value: Box::new(parser::nodes::Expression::Var(parser::nodes::Identifier { value: "x".to_string() })), + return_value: Box::new(parser::nodes::Expression::Var(parser::nodes::Identifier { value: "x".to_string() }, Some(parser::nodes::Type::I32))), })), ], })), @@ -321,14 +437,14 @@ mod tests { let program = tacky.generate(); assert_eq!(program.function_definitions.len(), 1); assert_eq!(program.function_definitions[0].function_name, "main"); - assert_eq!(program.function_definitions[0].return_type, nodes::Type::Int); + assert_eq!(program.function_definitions[0].return_type, nodes::Type::I32); assert_eq!(program.function_definitions[0].body.instructions.len(), 3); assert_eq!(program.function_definitions[0].body.instructions[0], nodes::Instruction::Copy(nodes::Copy { - src: nodes::Value::Constant(42), + src: nodes::Value::Constant(nodes::Constant::I32(42)), dest: nodes::Value::Identifier("x".to_string()), })); assert_eq!(program.function_definitions[0].body.instructions[1], nodes::Instruction::Return(nodes::Value::Identifier("x".to_string()))); - assert_eq!(program.function_definitions[0].body.instructions[2], nodes::Instruction::Return(nodes::Value::Constant(0))); // Return 0 is added by the generator so if theres no return statement it doesnt bug out + assert_eq!(program.function_definitions[0].body.instructions[2], nodes::Instruction::Return(nodes::Value::Constant(nodes::Constant::I32(0)))); // Return 0 is added by the generator so if theres no return statement it doesnt bug out } #[test] @@ -337,11 +453,11 @@ mod tests { function_definitions: vec![ parser::nodes::FunctionDeclaration { function_name: "main".to_string(), - return_type: parser::nodes::Type::Int, + return_type: parser::nodes::Type::I32, body: Box::new(parser::nodes::Statement::Compound(parser::nodes::CompoundStatement { statements: vec![ Box::new(parser::nodes::Statement::ReturnStatement(parser::nodes::ReturnStatement { - return_value: Box::new(parser::nodes::Expression::UnaryOp(parser::nodes::UnaryOp::Negation, Box::new(parser::nodes::Expression::Literal(parser::nodes::Literal::Int(42))))), + return_value: Box::new(parser::nodes::Expression::UnaryOp(parser::nodes::UnaryOp::Negation, Box::new(parser::nodes::Expression::Literal(parser::nodes::Literal::I32(42), Some(parser::nodes::Type::I32))), Some(parser::nodes::Type::I32))), })), ], })), @@ -353,14 +469,14 @@ mod tests { let program = tacky.generate(); assert_eq!(program.function_definitions.len(), 1); assert_eq!(program.function_definitions[0].function_name, "main"); - assert_eq!(program.function_definitions[0].return_type, nodes::Type::Int); + assert_eq!(program.function_definitions[0].return_type, nodes::Type::I32); assert_eq!(program.function_definitions[0].body.instructions.len(), 3); assert_eq!(program.function_definitions[0].body.instructions[0], nodes::Instruction::Unary(nodes::Unary { operator: nodes::UnaryOperator::Negate, - src: nodes::Value::Constant(42), + src: nodes::Value::Constant(nodes::Constant::I32(42)), dest: nodes::Value::Identifier(".tmp0".to_string()), })); assert_eq!(program.function_definitions[0].body.instructions[1], nodes::Instruction::Return(nodes::Value::Identifier(".tmp0".to_string()))); - assert_eq!(program.function_definitions[0].body.instructions[2], nodes::Instruction::Return(nodes::Value::Constant(0))); // Return 0 is added by the generator so if theres no return statement it doesnt bug out + assert_eq!(program.function_definitions[0].body.instructions[2], nodes::Instruction::Return(nodes::Value::Constant(nodes::Constant::I32(0)))); // Return 0 is added by the generator so if theres no return statement it doesnt bug out } } \ No newline at end of file diff --git a/src/tacky/nodes.rs b/src/tacky/nodes.rs index 18a0c2b..cbff906 100644 --- a/src/tacky/nodes.rs +++ b/src/tacky/nodes.rs @@ -1,5 +1,7 @@ #![allow(dead_code)] +use std::collections::HashMap; + #[derive(Debug, Clone, PartialEq)] pub struct Program { pub function_definitions: Vec, @@ -29,6 +31,8 @@ pub enum Instruction { JumpIfNotZero(String, Value), Label(String), FunCall(FunCall), + SignExtend(Value, Value), + Truncate(Value, Value), } #[derive(Debug, Clone, PartialEq)] @@ -84,13 +88,108 @@ pub enum BinaryOperator { #[derive(Debug, Clone, PartialEq)] pub enum Value { Identifier(String), - Constant(i32), + Constant(Constant), Empty, } +impl Value { + pub fn is_empty(&self) -> bool { + match self { + Value::Empty => true, + _ => false, + } + } + + pub fn is_identifier(&self) -> bool { + match self { + Value::Identifier(_) => true, + _ => false, + } + } + + pub fn as_identifier(&self) -> &str { + match self { + Value::Identifier(val) => val, + _ => panic!("Value is not an identifier"), + } + } + + pub fn is_constant(&self) -> bool { + match self { + Value::Constant(_) => true, + _ => false, + } + } + + pub fn as_constant(&self) -> &Constant { + match self { + Value::Constant(val) => val, + _ => panic!("Value is not a constant"), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Constant { + I32(i32), + I64(i64), +} + +impl Constant { + pub fn as_i32(&self) -> i32 { + match self { + Constant::I32(val) => *val, + Constant::I64(val) => *val as i32, + } + } + + pub fn as_i64(&self) -> i64 { + match self { + Constant::I64(val) => *val, + Constant::I32(val) => *val as i64, + } + } + + pub fn is_i32(&self) -> bool { + match self { + Constant::I32(_) => true, + _ => false, + } + } + + pub fn is_i64(&self) -> bool { + match self { + Constant::I64(_) => true, + _ => false, + } + } +} + #[derive(Debug, Clone, PartialEq)] pub enum Type { - Int, + I32, + I64, Fn(Vec, Box), Identifier(String), +} + +#[derive(Debug, Clone, PartialEq)] +pub struct SymbolTable { + pub table: HashMap, +} + +impl SymbolTable { + pub fn new() -> Self { + Self { + table: HashMap::new(), + } + } + + pub fn insert(&mut self, key: String, value: Type) { + self.table.insert(key, value); + } + + pub fn get(&self, key: &str) -> Option<&Type> { + self.table.get(key) + } } \ No newline at end of file diff --git a/test.ag b/test.ag index 35d224d..43379b3 100644 --- a/test.ag +++ b/test.ag @@ -1,14 +1,7 @@ -fn recurse(amount: int, i: int, res: int) -> int { - if amount != i { - return recurse(amount, i+1, res*i); - } - return res*i; -} +fn main() -> i32 { + let abc: i64 = 5; -fn recurse_start(amount: int) -> int { - return recurse(amount, 1, 1) - 1; -} + let abc32: i32 = abc; -fn main(argc: int) -> int { - return recurse_start(10); + return abc32; } \ No newline at end of file diff --git a/todo.md b/todo.md index b5e2edf..bca6438 100644 --- a/todo.md +++ b/todo.md @@ -22,7 +22,7 @@ wow todos for the argent language - [ ] i8 - [ ] i16 - [x] int (can also be i32) - - [ ] i64 + - [x] i64 (aka long, you cant use "long" tho) - [ ] isize - [ ] u8 - [ ] u16