diff --git a/src/compiler.rs b/src/compiler.rs index 0231340..2ae24db 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1,6 +1,8 @@ use std::io; use interpreter::interpret; +use ir::IrInstruction; +use ir_generator::generate_ir; use parser::parse; use symtab::SymTab; use tokenizer::tokenize; @@ -8,6 +10,8 @@ use type_checker::type_check; mod ast; mod interpreter; +mod ir; +mod ir_generator; mod parser; mod symtab; mod token; @@ -15,10 +19,20 @@ mod tokenizer; mod type_checker; mod variable; -pub fn compile(code: &str) { +pub fn compile(code: &str) -> Vec { let tokens = tokenize(code); let mut ast = parse(&tokens); type_check(&mut ast, &mut SymTab::new_type_table()); + generate_ir(&ast) +} + +pub fn start_compiler() { + let lines = io::stdin().lines(); + for line in lines.map_while(Result::ok) { + for instruction in compile(&line) { + println!("{instruction}"); + } + } } pub fn start_interpreter() { diff --git a/src/compiler/ir.rs b/src/compiler/ir.rs new file mode 100644 index 0000000..5bd9828 --- /dev/null +++ b/src/compiler/ir.rs @@ -0,0 +1,98 @@ +use std::{collections::HashMap, fmt}; + +use crate::compiler::{token::CodeLocation, variable::Type}; + +#[derive(PartialEq, Clone, Eq, Hash)] +pub struct IrVar { + pub name: String, +} + +impl fmt::Debug for IrVar { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.name) + } +} + +impl fmt::Display for IrVar { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.name) + } +} + +impl IrVar { + pub fn new(name: &str) -> Self { + Self { + name: name.to_owned(), + } + } + + pub fn new_global_types() -> HashMap { + use Type::*; + HashMap::from([ + (IrVar::new("unit"), Unit), + (IrVar::new("print_bool"), Func(vec![Bool], Box::new(Unit))), + (IrVar::new("print_int"), Func(vec![Int], Box::new(Unit))), + (IrVar::new("read_int"), Func(vec![], Box::new(Int))), + (IrVar::new("+"), Func(vec![Int, Int], Box::new(Int))), + (IrVar::new("*"), Func(vec![Int, Int], Box::new(Int))), + (IrVar::new("-"), Func(vec![Int, Int], Box::new(Int))), + (IrVar::new("/"), Func(vec![Int, Int], Box::new(Int))), + (IrVar::new("%"), Func(vec![Int, Int], Box::new(Int))), + (IrVar::new("<"), Func(vec![Int, Int], Box::new(Bool))), + (IrVar::new("<="), Func(vec![Int, Int], Box::new(Bool))), + (IrVar::new(">"), Func(vec![Int, Int], Box::new(Bool))), + (IrVar::new(">="), Func(vec![Int, Int], Box::new(Bool))), + (IrVar::new("not"), Func(vec![Bool], Box::new(Bool))), + (IrVar::new("neg"), Func(vec![Int], Box::new(Int))), + (IrVar::new("or"), Func(vec![Bool, Bool], Box::new(Bool))), + (IrVar::new("and"), Func(vec![Bool, Bool], Box::new(Bool))), + ]) + } +} + +impl fmt::Display for IrInstruction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} # From {}", self.instruction, self.loc) + } +} + +#[derive(Debug, PartialEq, Clone)] +pub struct IrInstruction { + pub loc: CodeLocation, + pub instruction: IrInstructionType, +} + +impl IrInstruction { + pub fn new(loc: CodeLocation, instruction: IrInstructionType) -> Self { + Self { loc, instruction } + } +} + +#[derive(Debug, PartialEq, Clone)] +pub enum IrInstructionType { + LoadBoolConst(bool, IrVar), + LoadIntConst(i64, IrVar), + Copy(IrVar, IrVar), + Call(IrVar, Vec, IrVar), + Jump(Box), + CondJump(IrVar, Box, Box), + Label(String), +} + +impl fmt::Display for IrInstructionType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let string = match self { + IrInstructionType::LoadBoolConst(val, dest) => format!("LoadBoolConst({val}, {dest})"), + IrInstructionType::LoadIntConst(val, dest) => format!("LoadIntConst({val}, {dest})"), + IrInstructionType::Copy(src, dest) => format!("Copy({src}, {dest})"), + IrInstructionType::Call(f, args, res) => format!("Call({f}, {args:?}, {res})"), + IrInstructionType::Jump(dest) => format!("Jump({})", *dest), + IrInstructionType::CondJump(cond, then_dest, else_dest) => { + format!("CondJump({cond}, {then_dest}, {else_dest})") + } + IrInstructionType::Label(name) => format!("Label({name})"), + }; + + write!(f, "{}", string) + } +} diff --git a/src/compiler/ir_generator.rs b/src/compiler/ir_generator.rs new file mode 100644 index 0000000..28c5f74 --- /dev/null +++ b/src/compiler/ir_generator.rs @@ -0,0 +1,115 @@ +use std::collections::HashMap; + +use crate::compiler::{ + ast::{AstNode, Expression::*}, + ir::{IrInstruction, IrInstructionType::*, IrVar}, + symtab::SymTab, + variable::Type, +}; + +pub fn generate_ir(ast: &AstNode) -> Vec { + let mut instructions = Vec::new(); + + let mut symbols = SymTab::new(); + let global_types = IrVar::new_global_types(); + let mut types = global_types.clone(); + for var in global_types.keys() { + symbols.insert(&var.name, var.clone()); + } + + let result = visit_ast_node(ast, &mut types, &mut symbols, &mut instructions); + + match types.get(&result) { + Some(Type::Int) => { + let loc = instructions.last().unwrap().loc; + let fn_var = symbols.get("print_int").clone(); + + instructions.push(IrInstruction::new( + loc, + Call(fn_var, vec![result], symbols.get("unit").clone()), + )); + } + Some(Type::Bool) => { + let loc = instructions.last().unwrap().loc; + let fn_var = symbols.get("print_bool").clone(); + + instructions.push(IrInstruction::new( + loc, + Call(fn_var, vec![result], symbols.get("unit").clone()), + )); + } + _ => (), + } + + instructions +} + +fn add_var(var_type: &Type, types: &mut HashMap) -> IrVar { + let mut i = 1; + //let type_str = match var_type { + // Type::Int => "i", + // Type::Bool => "b", + // Type::Func(_, _) => "f", + // Type::Unit => "u", + //}; + let type_str = "x"; + + let mut var = IrVar::new(&format!("{}{}", type_str, i)); + + while types.contains_key(&var) { + i += 1; + var = IrVar::new(&format!("{}{}", type_str, i)); + } + + types.insert(var.clone(), var_type.clone()); + var +} + +fn visit_ast_node( + ast: &AstNode, + types: &mut HashMap, + symbols: &mut SymTab, + instructions: &mut Vec, +) -> IrVar { + match &ast.expr { + EmptyLiteral() => symbols.get("unit").clone(), + IntLiteral(val) => { + let var = add_var(&Type::Int, types); + instructions.push(IrInstruction::new(ast.loc, LoadIntConst(*val, var.clone()))); + var + } + BoolLiteral(val) => { + let var = add_var(&Type::Bool, types); + instructions.push(IrInstruction { + loc: ast.loc, + instruction: LoadBoolConst(*val, var.clone()), + }); + var + } + Identifier(name) => symbols.get(name).clone(), + UnaryOp(_, _) => todo!(), + BinaryOp(left, op, right) => match *op { + "=" => todo!(), // TODO Special handling + "and" => todo!(), // TODO Special handling + "or" => todo!(), // TODO Special handling + _ => { + let op_var = symbols.get(op).clone(); + let left_var = visit_ast_node(left, types, symbols, instructions); + let right_var = visit_ast_node(right, types, symbols, instructions); + let result_var = add_var(&ast.node_type, types); + + instructions.push(IrInstruction::new( + ast.loc, + Call(op_var, vec![left_var, right_var], result_var.clone()), + )); + + result_var + } + }, + VarDeclaration(_, _, _) => todo!(), + Conditional(_, _, _) => todo!(), + While(_, _) => todo!(), + FunCall(_, _) => todo!(), + Block(_) => todo!(), + } +} diff --git a/src/compiler/symtab.rs b/src/compiler/symtab.rs index a1cae10..2e3c031 100644 --- a/src/compiler/symtab.rs +++ b/src/compiler/symtab.rs @@ -37,6 +37,14 @@ impl<'source, T> SymTab<'source, T> { } } +impl<'source, T> SymTab<'source, T> { + pub fn new() -> SymTab<'source, T> { + SymTab { + tables: vec![HashMap::new()], + } + } +} + impl<'source> SymTab<'source, Type> { pub fn new_type_table() -> SymTab<'source, Type> { use Type::*; diff --git a/src/main.rs b/src/main.rs index 3d6985d..aa3211f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,8 @@ fn main() { if let Some(flag) = args.get(1) { if flag == "-i" { compiler::start_interpreter(); + } else if flag == "-c" { + compiler::start_compiler(); } } else { server::start("::".parse().unwrap(), 3000);