Add first part of IR Generator implementation
This commit is contained in:
parent
f059870e81
commit
dc50ce64eb
5 changed files with 238 additions and 1 deletions
|
@ -1,6 +1,8 @@
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use interpreter::interpret;
|
use interpreter::interpret;
|
||||||
|
use ir::IrInstruction;
|
||||||
|
use ir_generator::generate_ir;
|
||||||
use parser::parse;
|
use parser::parse;
|
||||||
use symtab::SymTab;
|
use symtab::SymTab;
|
||||||
use tokenizer::tokenize;
|
use tokenizer::tokenize;
|
||||||
|
@ -8,6 +10,8 @@ use type_checker::type_check;
|
||||||
|
|
||||||
mod ast;
|
mod ast;
|
||||||
mod interpreter;
|
mod interpreter;
|
||||||
|
mod ir;
|
||||||
|
mod ir_generator;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod symtab;
|
mod symtab;
|
||||||
mod token;
|
mod token;
|
||||||
|
@ -15,10 +19,20 @@ mod tokenizer;
|
||||||
mod type_checker;
|
mod type_checker;
|
||||||
mod variable;
|
mod variable;
|
||||||
|
|
||||||
pub fn compile(code: &str) {
|
pub fn compile(code: &str) -> Vec<IrInstruction> {
|
||||||
let tokens = tokenize(code);
|
let tokens = tokenize(code);
|
||||||
let mut ast = parse(&tokens);
|
let mut ast = parse(&tokens);
|
||||||
type_check(&mut ast, &mut SymTab::new_type_table());
|
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() {
|
pub fn start_interpreter() {
|
||||||
|
|
98
src/compiler/ir.rs
Normal file
98
src/compiler/ir.rs
Normal file
|
@ -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<IrVar, Type> {
|
||||||
|
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>, IrVar),
|
||||||
|
Jump(Box<IrInstruction>),
|
||||||
|
CondJump(IrVar, Box<IrInstruction>, Box<IrInstruction>),
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
115
src/compiler/ir_generator.rs
Normal file
115
src/compiler/ir_generator.rs
Normal file
|
@ -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<IrInstruction> {
|
||||||
|
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, Type>) -> 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<IrVar, Type>,
|
||||||
|
symbols: &mut SymTab<IrVar>,
|
||||||
|
instructions: &mut Vec<IrInstruction>,
|
||||||
|
) -> 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!(),
|
||||||
|
}
|
||||||
|
}
|
|
@ -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> {
|
impl<'source> SymTab<'source, Type> {
|
||||||
pub fn new_type_table() -> SymTab<'source, Type> {
|
pub fn new_type_table() -> SymTab<'source, Type> {
|
||||||
use Type::*;
|
use Type::*;
|
||||||
|
|
|
@ -9,6 +9,8 @@ fn main() {
|
||||||
if let Some(flag) = args.get(1) {
|
if let Some(flag) = args.get(1) {
|
||||||
if flag == "-i" {
|
if flag == "-i" {
|
||||||
compiler::start_interpreter();
|
compiler::start_interpreter();
|
||||||
|
} else if flag == "-c" {
|
||||||
|
compiler::start_compiler();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
server::start("::".parse().unwrap(), 3000);
|
server::start("::".parse().unwrap(), 3000);
|
||||||
|
|
Reference in a new issue