diff --git a/examples/invalid/undeclared-variable-if.ra b/examples/invalid/undeclared-variable-if.ra new file mode 100644 index 0000000..d335175 --- /dev/null +++ b/examples/invalid/undeclared-variable-if.ra @@ -0,0 +1,5 @@ +func main(): void { + if (a < 2) { + print(2); + } +} diff --git a/examples/invalid/undeclared-variable-while.ra b/examples/invalid/undeclared-variable-while.ra new file mode 100644 index 0000000..4b28c65 --- /dev/null +++ b/examples/invalid/undeclared-variable-while.ra @@ -0,0 +1,6 @@ +func main(): void { + while (a < 2) { + print(2); + } +} + diff --git a/examples/valid/condition.ra b/examples/valid/condition.ra new file mode 100644 index 0000000..13ed043 --- /dev/null +++ b/examples/valid/condition.ra @@ -0,0 +1,18 @@ +func main(): void { + if (1 < 2) { + a = 1; + } else if (2 < 3) { + a = 2; + } else { + a = 3; + } + if (1 > 2) { + b = 1; + } else { + b = 2; + } + if (1 == 1) { + c = 1; + } + print(a, b, c); +} diff --git a/examples/valid/for.ra b/examples/valid/for.ra new file mode 100644 index 0000000..6d2e088 --- /dev/null +++ b/examples/valid/for.ra @@ -0,0 +1,8 @@ +func main(): void { + b = 5; + for (a = 1 to b + 1) { + c = a; + print(c); + } + print(a, b); +} diff --git a/examples/valid/while.ra b/examples/valid/while.ra new file mode 100644 index 0000000..1f158b5 --- /dev/null +++ b/examples/valid/while.ra @@ -0,0 +1,7 @@ +func main(): void { + a = 1; + while (a < 10) { + a = a + 1; + } + print(a); +} diff --git a/src/ast/ast_kind.rs b/src/ast/ast_kind.rs index 6502d9c..ce50964 100644 --- a/src/ast/ast_kind.rs +++ b/src/ast/ast_kind.rs @@ -41,6 +41,23 @@ pub enum AstNodeKind<'a> { exprs: Vec>, }, Read, + Decision { + expr: Box>, + statements: Vec>, + else_block: Option>>, + }, + ElseBlock { + statements: Vec>, + }, + While { + expr: Box>, + statements: Vec>, + }, + For { + assignment: Box>, + expr: Box>, + statements: Vec>, + }, } impl<'a> From> for String { @@ -49,6 +66,7 @@ impl<'a> From> for String { AstNodeKind::Integer(n) => n.to_string(), AstNodeKind::Id(s) => s.to_string(), AstNodeKind::String(s) => s.to_string(), + AstNodeKind::Assignment { name, .. } => name, node => unreachable!("Node {:?}, cannot be a string", node), } } @@ -96,6 +114,26 @@ impl fmt::Debug for AstNodeKind<'_> { AstNodeKind::BinaryOperation { operator, lhs, rhs } => { write!(f, "BinaryOperation({:?}, {:?}, {:?})", operator, lhs, rhs) } + AstNodeKind::Decision { + expr, + statements, + else_block, + } => { + write!(f, "Decision({expr:?}, {statements:?}, {else_block:?})") + } + AstNodeKind::ElseBlock { statements } => { + write!(f, "ElseBlock({:?})", statements) + } + AstNodeKind::While { expr, statements } => { + write!(f, "While({:?}, {:?})", expr, statements) + } + AstNodeKind::For { + expr, + statements, + assignment, + } => { + write!(f, "For({expr:?}, {statements:?}, {assignment:?})") + } } } } diff --git a/src/ast/mod.rs b/src/ast/mod.rs index d9bac16..87baaab 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -16,6 +16,35 @@ impl<'a> From> for String { } } +impl<'a> AstNode<'a> { + pub fn expand_node(v: AstNode<'a>) -> Vec> { + let node = v.clone(); + match &v.kind { + AstNodeKind::Decision { statements, .. } + | AstNodeKind::ElseBlock { statements } + | AstNodeKind::While { statements, .. } => statements + .to_owned() + .into_iter() + .flat_map(AstNode::expand_node) + .collect(), + AstNodeKind::For { + statements, + assignment, + .. + } => vec![*assignment.clone()] + .to_owned() + .into_iter() + .chain(statements.to_owned()) + .flat_map(AstNode::expand_node) + .collect(), + _ => vec![node], + } + } + pub fn new(kind: AstNodeKind<'a>, span: Span<'a>) -> AstNode<'a> { + AstNode { kind, span } + } +} + impl fmt::Debug for AstNode<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.kind) diff --git a/src/dir_func/function.rs b/src/dir_func/function.rs index 203e79d..e9b4689 100644 --- a/src/dir_func/function.rs +++ b/src/dir_func/function.rs @@ -83,7 +83,11 @@ impl Function { ) -> Results<'a, Self> { let errors: Vec = nodes .into_iter() - .filter_map(|node| self.insert_variable_from_node(node, global_fn).err()) + .flat_map(AstNode::expand_node) + .filter_map(|node| { + self.insert_variable_from_node(node.to_owned(), global_fn) + .err() + }) .collect(); if errors.is_empty() { Ok(self.to_owned()) diff --git a/src/enums/mod.rs b/src/enums/mod.rs index 4ad8860..300baee 100644 --- a/src/enums/mod.rs +++ b/src/enums/mod.rs @@ -1,3 +1,5 @@ +use core::fmt; + use crate::ast::ast_kind::AstNodeKind; use crate::ast::AstNode; use crate::dir_func::function::VariablesTable; @@ -14,7 +16,7 @@ pub enum Types { } impl Types { - fn is_boolish(&self) -> bool { + pub fn is_boolish(&self) -> bool { match self { Types::INT | Types::BOOL => true, _ => false, @@ -28,6 +30,14 @@ impl Types { } } + pub fn can_cast(&self, to: Types) -> bool { + match to { + Types::BOOL => self.is_boolish(), + Types::FLOAT => self.is_number(), + _ => to == self.to_owned(), + } + } + pub fn binary_operator_type( operator: Operator, lhs_type: Types, @@ -160,9 +170,27 @@ pub enum Operator { Minus, Times, Div, + Inc, // ByteCode Assignment, Print, PrintNl, Read, + Goto, + GotoF, +} + +impl Operator { + pub fn is_goto(&self) -> bool { + match self { + Operator::Goto | Operator::GotoF => true, + _ => false, + } + } +} + +impl fmt::Display for Operator { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:10}", format!("{:?}", self)) + } } diff --git a/src/main.rs b/src/main.rs index 1ee10a1..2b78cbd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,9 +9,11 @@ mod error; mod parser; mod quadruple; +use ast::AstNode; use dir_func::DirFunc; +use error::Results; use parser::parse; -use quadruple::QuadrupleManager; +use quadruple::quadruple_manager::QuadrupleManager; // ANCHOR: Testing the examples mod test_parser; @@ -22,6 +24,22 @@ use std::process::exit; use args::parse_args; +fn parse_ast<'a>(ast: AstNode<'a>, debug: bool) -> Results<'a, ()> { + let mut dir_func = DirFunc::new(); + dir_func.build_dir_func(ast.clone())?; + if debug { + println!("Dir func created sucessfully"); + println!("{:#?}", dir_func); + } + let mut quad_manager = QuadrupleManager::new(&mut dir_func); + quad_manager.parse(ast.clone())?; + Ok(if debug { + println!("Quads created sucessfully"); + println!("{:#?}", quad_manager.memory); + println!("{:?}", quad_manager); + }) +} + fn main() { let matches = parse_args(); let filename = matches.value_of("file").expect("required"); @@ -40,29 +58,12 @@ fn main() { println!("Parsing ended sucessfully"); println!("AST:\n{:?}", ast); } - let mut dir_func = DirFunc::new(); - if let Err(errors) = dir_func.build_dir_func(ast.clone()) { + if let Err(errors) = parse_ast(ast, debug) { for error in errors { println!("{:?}", error); } exit(1); } - if debug { - println!("Dir func created sucessfully"); - println!("{:#?}", dir_func); - } - let mut quad_manager = QuadrupleManager::new(&mut dir_func); - if let Err(errors) = quad_manager.parse(ast.clone()) { - for error in errors { - println!("{:?}", error); - } - exit(1); - } - if debug { - println!("Quads created sucessfully"); - println!("{:#?}", quad_manager.memory); - println!("{:#?}", quad_manager.quad_list); - } } #[cfg(test)] diff --git a/src/parser/grammar.pest b/src/parser/grammar.pest index a43681a..5e554f8 100644 --- a/src/parser/grammar.pest +++ b/src/parser/grammar.pest @@ -151,16 +151,16 @@ function = { FUNC_HEADER ~ block } MAIN_FUNCTION = _{ FUNC ~ MAIN ~ L_PAREN ~ R_PAREN ~ COLON ~ void ~ block } FUNC_CALL = { id ~ L_PAREN ~ exprs? ~ R_PAREN } -COND_EXPR = { L_PAREN ~ expr ~ R_PAREN } -IF_BLOCK = { IF ~ COND_EXPR ~ block } -ELSE_BLOCK = { ELSE ~ (block | DECISION) } -DECISION = { IF_BLOCK ~ ELSE_BLOCK? } +COND_EXPR = _{ L_PAREN ~ expr ~ R_PAREN } +if_block = _{ IF ~ COND_EXPR ~ block } +else_block = { ELSE ~ (block | decision) } +decision = { if_block ~ else_block? } write = {PRINT ~ L_PAREN ~ exprs? ~ R_PAREN } -WHILE_LOOP = {WHILE ~ COND_EXPR ~ block} +while_loop = {WHILE ~ COND_EXPR ~ block} -FOR_LOOP = {FOR ~ L_PAREN ~ assignment ~ TO ~ expr ~ R_PAREN ~ block} +for_loop = {FOR ~ L_PAREN ~ assignment ~ TO ~ expr ~ R_PAREN ~ block} POSSIBLE_STR = {STRING_CTE | NON_CTE} READ_CSV_EXTRA = {(COMMA ~ id){2}} @@ -179,7 +179,7 @@ DATAFRAME_VOID_OPS = _{PLOT | HISTOGRAM} RETURN = { RETURN_KEY ~ expr } -BLOCK_STATEMENT = _{ DECISION | WHILE_LOOP | FOR_LOOP } +BLOCK_STATEMENT = _{ decision | while_loop | for_loop } INLINE_STATEMENT = _{ DATAFRAME_VOID_OPS | assignment | write | RETURN | FUNC_CALL } statement = { INLINE_STATEMENT ~ SEMI_COLON | BLOCK_STATEMENT } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index b1ec817..bd15851 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -331,6 +331,73 @@ impl LanguageParser { )) } + // Condition + fn else_block(input: Node) -> Result { + let span = input.as_span().clone(); + Ok(match_nodes!(input.into_children(); + [block(statements)] => { + let kind = AstNodeKind::ElseBlock { statements }; + AstNode {kind, span} + }, + [decision(decision)] => decision, + )) + } + + fn decision(input: Node) -> Result { + let span = input.as_span().clone(); + Ok(match_nodes!(input.into_children(); + [expr(expr), block(statements)] => { + let kind = AstNodeKind::Decision { + expr: Box::new(expr), + statements, + else_block: None + }; + AstNode {kind, span} + }, + [expr(expr), block(statements), else_block(else_block)] => { + let kind = AstNodeKind::Decision { + expr: Box::new(expr), + statements, + else_block: Some(Box::new(else_block)) + }; + AstNode {kind, span} + }, + )) + } + + // Loops + fn while_loop(input: Node) -> Result { + let span = input.as_span().clone(); + Ok(match_nodes!(input.into_children(); + [expr(expr), block(statements)] => { + let kind = AstNodeKind::While { + expr: Box::new(expr), + statements, + }; + AstNode {kind, span} + }, + )) + } + + fn for_loop(input: Node) -> Result { + let span = input.as_span().clone(); + Ok(match_nodes!(input.into_children(); + [assignment(assignment), expr(stop_expr), block(statements)] => { + let assignment_clone = assignment.clone(); + let expr_clone = stop_expr.clone(); + let id_node = AstNode::new(AstNodeKind::Id(String::from(assignment_clone.kind)), assignment_clone.span); + let expr_kind = AstNodeKind::BinaryOperation { + operator: Operator::Lt, + lhs: Box::new(id_node), + rhs: Box::new(stop_expr), + }; + let expr = Box::new(AstNode::new(expr_kind, expr_clone.span)); + let kind = AstNodeKind::For { assignment: Box::new(assignment), expr, statements }; + AstNode::new(kind, span) + }, + )) + } + // Inline statements fn assignment(input: Node) -> Result { let span = input.as_span().clone(); @@ -363,6 +430,9 @@ impl LanguageParser { Ok(match_nodes!(input.into_children(); [assignment(node)] => node, [write(node)] => node, + [decision(node)] => node, + [while_loop(node)] => node, + [for_loop(node)] => node, )) } @@ -372,6 +442,7 @@ impl LanguageParser { )) } + // Function fn func_arg(input: Node) -> Result { let span = input.as_span().clone(); Ok(match_nodes!(input.into_children(); @@ -388,7 +459,6 @@ impl LanguageParser { )) } - // Function fn function(input: Node) -> Result { // TODO: Still misses some conditions if *input.user_data() { diff --git a/src/quadruple/mod.rs b/src/quadruple/mod.rs index 7260a4f..2cf01f7 100644 --- a/src/quadruple/mod.rs +++ b/src/quadruple/mod.rs @@ -1,271 +1,2 @@ -use std::fmt; - -use crate::{ - address::{Address, ConstantMemory, GenericAddressManager}, - ast::{ast_kind::AstNodeKind, AstNode}, - dir_func::{ - function::{Function, VariablesTable}, - variable_value::VariableValue, - DirFunc, - }, - enums::{Operator, Types}, - error::{error_kind::RaoulErrorKind, RaoulError, Result, Results}, -}; - -#[derive(Clone, Copy, PartialEq, Hash, Eq)] -pub struct Quadruple { - operator: Operator, - op_1: Option, - op_2: Option, - res: Option, -} - -impl Quadruple { - fn format_address(option: Option) -> String { - match option { - None => "-".to_owned(), - Some(address) => address.to_string(), - } - } -} - -impl fmt::Debug for Quadruple { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{:?} {} {} {}", - self.operator, - Quadruple::format_address(self.op_1), - Quadruple::format_address(self.op_2), - Quadruple::format_address(self.res), - ) - } -} - -#[derive(Debug, PartialEq)] -pub struct QuadrupleManager<'a> { - dir_func: &'a mut DirFunc, - function_name: String, - pub memory: ConstantMemory, - pub quad_list: Vec, -} - -impl QuadrupleManager<'_> { - pub fn new<'a>(dir_func: &'a mut DirFunc) -> QuadrupleManager<'a> { - QuadrupleManager { - dir_func, - function_name: "".to_owned(), - memory: ConstantMemory::new(), - quad_list: Vec::new(), - } - } - - fn function(&self) -> &Function { - self.dir_func - .functions - .get(&self.function_name) - .expect(&self.function_name) - } - - fn function_variables(&self) -> &VariablesTable { - &self.function().variables - } - - fn global_variables(&self) -> &VariablesTable { - &self.dir_func.global_fn.variables - } - - fn get_variable_address(&self, global: bool, name: &str) -> usize { - let variables = match global { - true => self.global_variables(), - false => self.function_variables(), - }; - variables.get(name).expect(name).address - } - - fn function_mut(&mut self) -> &mut Function { - self.dir_func - .functions - .get_mut(&self.function_name) - .unwrap() - } - - fn add_temp(&mut self, data_type: &Types) -> Option { - self.function_mut().temp_addresses.get_address(data_type) - } - - fn safe_address<'a, T>(&self, option: Option, node: AstNode<'a>) -> Result<'a, T> { - match option { - Some(value) => Ok(value), - None => Err(RaoulError::new(node, RaoulErrorKind::MemoryExceded)), - } - } - - fn safe_add_temp<'a>(&mut self, data_type: &Types, node: AstNode<'a>) -> Result<'a, usize> { - let option = self.add_temp(data_type); - self.safe_address(option, node) - } - - fn safe_remove_temp_address(&mut self, option: Option) { - match option.is_temp_address() { - false => (), - true => self - .function_mut() - .temp_addresses - .release_address(option.unwrap()), - } - } - - fn add_quad(&mut self, quad: Quadruple) { - self.quad_list.push(quad); - self.safe_remove_temp_address(quad.op_1); - self.safe_remove_temp_address(quad.op_2); - } - - fn parse_expr<'a>(&mut self, node: AstNode<'a>) -> Result<'a, (usize, Types)> { - let node_clone = node.clone(); - match node.kind { - AstNodeKind::Bool(_) - | AstNodeKind::Float(_) - | AstNodeKind::Integer(_) - | AstNodeKind::String(_) => { - let value = VariableValue::from(node.kind); - let result = self.memory.add(value); - self.safe_address(result, node_clone) - } - AstNodeKind::UnaryOperation { operator, operand } => { - let (op, op_type) = self.parse_expr(*operand)?; - let res_type = match operator { - Operator::Not => match op_type { - Types::BOOL | Types::INT => Types::BOOL, - op_type => { - let kind = RaoulErrorKind::InvalidCast { - from: op_type, - to: Types::BOOL, - }; - return Err(RaoulError::new(node_clone, kind)); - } - }, - _ => unreachable!(), - }; - let res = self.safe_add_temp(&res_type, node_clone)?; - let quad = Quadruple { - operator, - op_1: Some(op), - op_2: None, - res: Some(res), - }; - self.add_quad(quad); - Ok((res, res_type)) - } - AstNodeKind::Id(name) => { - match self - .function_variables() - .get(&name) - .or(self.global_variables().get(&name)) - { - Some(variable) => Ok((variable.address, variable.data_type)), - None => unreachable!(), - } - } - AstNodeKind::Read => { - let data_type = Types::STRING; - let res = self.safe_add_temp(&data_type, node_clone)?; - self.add_quad(Quadruple { - operator: Operator::Read, - op_1: None, - op_2: None, - res: Some(res), - }); - Ok((res, data_type)) - } - AstNodeKind::BinaryOperation { operator, lhs, rhs } => { - let (op_1, lhs_type) = self.parse_expr(*lhs)?; - let (op_2, rhs_type) = self.parse_expr(*rhs)?; - let data_type = Types::binary_operator_type(operator, lhs_type, rhs_type).unwrap(); - let res = self.safe_add_temp(&data_type, node_clone)?; - self.add_quad(Quadruple { - operator, - op_1: Some(op_1), - op_2: Some(op_2), - res: Some(res), - }); - Ok((res, data_type)) - } - kind => unreachable!("{:?}", kind), - } - } - - fn parse_function<'a>(&mut self, node: AstNode<'a>) -> Results<'a, ()> { - match node.kind { - AstNodeKind::Assignment { - global, - ref name, - value, - } => { - let result = self.parse_expr(*value); - if let Err(error) = result { - return Err(vec![error]); - } - let (value_addr, _) = result.unwrap(); - let variable_address = self.get_variable_address(global, name); - self.add_quad(Quadruple { - operator: Operator::Assignment, - op_1: Some(value_addr), - op_2: None, - res: Some(variable_address), - }); - Ok(()) - } - AstNodeKind::Write { exprs } => { - let (addresses, errors): (Vec<_>, Vec<_>) = exprs - .into_iter() - .map(|node| self.parse_expr(node)) - .partition(Result::is_ok); - let errors: Vec<_> = errors.into_iter().map(Result::unwrap_err).collect(); - if !errors.is_empty() { - return Err(errors); - } - addresses - .into_iter() - .map(Result::unwrap) - .for_each(|(address, _)| { - self.add_quad(Quadruple { - operator: Operator::Print, - op_1: Some(address), - op_2: None, - res: None, - }) - }); - self.add_quad(Quadruple { - operator: Operator::PrintNl, - op_1: None, - op_2: None, - res: None, - }); - Ok(()) - } - _ => unreachable!(), - } - } - - pub fn parse<'a>(&mut self, node: AstNode<'a>) -> Results<'a, ()> { - match node.kind { - AstNodeKind::Main { body, .. } => { - self.function_name = "main".to_owned(); - let errors: Vec = body - .into_iter() - .filter_map(|node| self.parse_function(node).err()) - .flatten() - .collect(); - if errors.is_empty() { - Ok(()) - } else { - Err(errors) - } - } - AstNodeKind::Function { .. } => todo!(), - _ => unreachable!(), - } - } -} +pub mod quadruple; +pub mod quadruple_manager; diff --git a/src/quadruple/quadruple.rs b/src/quadruple/quadruple.rs new file mode 100644 index 0000000..a019e30 --- /dev/null +++ b/src/quadruple/quadruple.rs @@ -0,0 +1,33 @@ +use std::fmt; + +use crate::enums::Operator; + +#[derive(Clone, Copy, PartialEq, Hash, Eq)] +pub struct Quadruple { + pub operator: Operator, + pub op_1: Option, + pub op_2: Option, + pub res: Option, +} + +impl Quadruple { + fn format_address(option: Option) -> String { + match option { + None => "-".to_owned(), + Some(address) => address.to_string(), + } + } +} + +impl fmt::Debug for Quadruple { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{} {:5} {:5} {:5}", + self.operator, + Quadruple::format_address(self.op_1), + Quadruple::format_address(self.op_2), + Quadruple::format_address(self.res), + ) + } +} diff --git a/src/quadruple/quadruple_manager.rs b/src/quadruple/quadruple_manager.rs new file mode 100644 index 0000000..8454f97 --- /dev/null +++ b/src/quadruple/quadruple_manager.rs @@ -0,0 +1,383 @@ +use std::fmt; + +use crate::{ + address::{Address, ConstantMemory, GenericAddressManager}, + ast::{ast_kind::AstNodeKind, AstNode}, + dir_func::{ + function::{Function, VariablesTable}, + variable_value::VariableValue, + DirFunc, + }, + enums::{Operator, Types}, + error::{error_kind::RaoulErrorKind, RaoulError, Result, Results}, + quadruple::quadruple::Quadruple, +}; + +#[derive(PartialEq)] +pub struct QuadrupleManager<'a> { + dir_func: &'a mut DirFunc, + function_name: String, + jump_list: Vec, + pub memory: ConstantMemory, + pub quad_list: Vec, +} + +impl QuadrupleManager<'_> { + pub fn new<'a>(dir_func: &'a mut DirFunc) -> QuadrupleManager<'a> { + QuadrupleManager { + dir_func, + function_name: "".to_owned(), + memory: ConstantMemory::new(), + quad_list: Vec::new(), + jump_list: Vec::new(), + } + } + + fn function(&self) -> &Function { + self.dir_func + .functions + .get(&self.function_name) + .expect(&self.function_name) + } + + fn function_variables(&self) -> &VariablesTable { + &self.function().variables + } + + fn global_variables(&self) -> &VariablesTable { + &self.dir_func.global_fn.variables + } + + fn get_variable_address(&self, global: bool, name: &str) -> usize { + let variables = match global { + true => self.global_variables(), + false => self.function_variables(), + }; + variables.get(name).expect(name).address + } + + fn function_mut(&mut self) -> &mut Function { + self.dir_func + .functions + .get_mut(&self.function_name) + .unwrap() + } + + fn add_temp(&mut self, data_type: &Types) -> Option { + self.function_mut().temp_addresses.get_address(data_type) + } + + fn safe_address<'a, T>(&self, option: Option, node: AstNode<'a>) -> Result<'a, T> { + match option { + Some(value) => Ok(value), + None => Err(RaoulError::new(node, RaoulErrorKind::MemoryExceded)), + } + } + + fn safe_add_temp<'a>(&mut self, data_type: &Types, node: AstNode<'a>) -> Result<'a, usize> { + let option = self.add_temp(data_type); + self.safe_address(option, node) + } + + fn safe_remove_temp_address(&mut self, option: Option) { + match option.is_temp_address() { + false => (), + true => self + .function_mut() + .temp_addresses + .release_address(option.unwrap()), + } + } + + fn add_quad(&mut self, quad: Quadruple) { + self.quad_list.push(quad); + self.safe_remove_temp_address(quad.op_1); + self.safe_remove_temp_address(quad.op_2); + } + + fn get_variable_name_address<'a>( + &mut self, + name: String, + node: AstNode<'a>, + ) -> Result<'a, (usize, Types)> { + match self + .function_variables() + .get(&name) + .or(self.global_variables().get(&name)) + { + Some(variable) => Ok((variable.address, variable.data_type)), + None => { + let kind = RaoulErrorKind::UndeclaredVar { name }; + Err(RaoulError::new(node, kind)) + } + } + } + + fn parse_expr<'a>(&mut self, node: AstNode<'a>) -> Result<'a, (usize, Types)> { + let node_clone = node.clone(); + match node.kind { + AstNodeKind::Bool(_) + | AstNodeKind::Float(_) + | AstNodeKind::Integer(_) + | AstNodeKind::String(_) => { + let value = VariableValue::from(node.kind); + let result = self.memory.add(value); + self.safe_address(result, node_clone) + } + AstNodeKind::UnaryOperation { operator, operand } => { + let (op, op_type) = self.parse_expr(*operand)?; + let res_type = match operator { + Operator::Not => match op_type { + Types::BOOL | Types::INT => Types::BOOL, + op_type => { + let kind = RaoulErrorKind::InvalidCast { + from: op_type, + to: Types::BOOL, + }; + return Err(RaoulError::new(node_clone, kind)); + } + }, + _ => unreachable!(), + }; + let res = self.safe_add_temp(&res_type, node_clone)?; + let quad = Quadruple { + operator, + op_1: Some(op), + op_2: None, + res: Some(res), + }; + self.add_quad(quad); + Ok((res, res_type)) + } + AstNodeKind::Id(name) => self.get_variable_name_address(name, node_clone), + AstNodeKind::Read => { + let data_type = Types::STRING; + let res = self.safe_add_temp(&data_type, node_clone)?; + self.add_quad(Quadruple { + operator: Operator::Read, + op_1: None, + op_2: None, + res: Some(res), + }); + Ok((res, data_type)) + } + AstNodeKind::BinaryOperation { operator, lhs, rhs } => { + let (op_1, lhs_type) = self.parse_expr(*lhs)?; + let (op_2, rhs_type) = self.parse_expr(*rhs)?; + let data_type = Types::binary_operator_type(operator, lhs_type, rhs_type).unwrap(); + let res = self.safe_add_temp(&data_type, node_clone)?; + self.add_quad(Quadruple { + operator, + op_1: Some(op_1), + op_2: Some(op_2), + res: Some(res), + }); + Ok((res, data_type)) + } + kind => unreachable!("{:?}", kind), + } + } + + fn parse_expr_results<'a>(&mut self, node: AstNode<'a>) -> Results<'a, (usize, Types)> { + match self.parse_expr(node) { + Ok(val) => Ok(val), + Err(error) => Err(vec![error]), + } + } + + fn assert_type<'a>(&self, from: Types, to: Types, node: AstNode<'a>) -> Results<'a, ()> { + match from.can_cast(to) { + true => Ok(()), + false => { + let error = RaoulError::new(node, RaoulErrorKind::InvalidCast { from, to }); + Err(vec![error]) + } + } + } + + fn assert_expr_type<'a>( + &mut self, + expr: AstNode<'a>, + to: Types, + ) -> Results<'a, (usize, Types)> { + let expr_clone = expr.clone(); + let (res_address, res_type) = self.parse_expr_results(expr)?; + self.assert_type(res_type, to, expr_clone)?; + Ok((res_address, res_type)) + } + + fn parse_body<'a>(&mut self, body: Vec>) -> Results<'a, ()> { + let errors: Vec = body + .into_iter() + .filter_map(|node| self.parse_function(node).err()) + .flatten() + .collect(); + if errors.is_empty() { + Ok(()) + } else { + Err(errors) + } + } + + fn add_goto(&mut self, goto_type: Operator, condition: Option) { + debug_assert!(goto_type.is_goto()); + self.jump_list.push(self.quad_list.len()); + self.add_quad(Quadruple { + operator: goto_type, + op_1: condition, + op_2: None, + res: None, + }) + } + + fn fill_goto_index(&mut self, index: usize) { + let res = self.quad_list.len(); + let mut quad = self.quad_list.get_mut(index).unwrap(); + debug_assert!(quad.operator.is_goto()); + quad.res = Some(res); + } + + fn fill_goto(&mut self) { + let index = self.jump_list.pop().unwrap(); + self.fill_goto_index(index); + } + + fn parse_function<'a>(&mut self, node: AstNode<'a>) -> Results<'a, ()> { + let node_clone = node.clone(); + match node.kind { + AstNodeKind::Assignment { + global, + ref name, + value, + } => { + let (value_addr, _) = self.parse_expr_results(*value)?; + let variable_address = self.get_variable_address(global, name); + Ok(self.add_quad(Quadruple { + operator: Operator::Assignment, + op_1: Some(value_addr), + op_2: None, + res: Some(variable_address), + })) + } + AstNodeKind::Write { exprs } => { + let (addresses, errors): (Vec<_>, Vec<_>) = exprs + .into_iter() + .map(|node| self.parse_expr(node)) + .partition(Result::is_ok); + let errors: Vec<_> = errors.into_iter().map(Result::unwrap_err).collect(); + if !errors.is_empty() { + return Err(errors); + } + addresses + .into_iter() + .map(Result::unwrap) + .for_each(|(address, _)| { + self.add_quad(Quadruple { + operator: Operator::Print, + op_1: Some(address), + op_2: None, + res: None, + }) + }); + Ok(self.add_quad(Quadruple { + operator: Operator::PrintNl, + op_1: None, + op_2: None, + res: None, + })) + } + AstNodeKind::Decision { + expr, + statements, + else_block, + } => { + let (res_address, _) = self.assert_expr_type(*expr, Types::BOOL)?; + self.add_goto(Operator::GotoF, Some(res_address)); + self.parse_body(statements)?; + Ok(if let Some(node) = else_block { + let index = self.jump_list.pop().unwrap(); + self.add_goto(Operator::Goto, None); + self.fill_goto_index(index); + self.parse_function(*node)?; + self.fill_goto(); + } else { + self.fill_goto(); + }) + } + AstNodeKind::ElseBlock { statements } => Ok(self.parse_body(statements)?), + AstNodeKind::While { expr, statements } => { + self.jump_list.push(self.quad_list.len()); + let (res_address, _) = self.assert_expr_type(*expr, Types::BOOL)?; + self.add_goto(Operator::GotoF, Some(res_address)); + self.parse_body(statements)?; + let index = self.jump_list.pop().unwrap(); + let goto_res = self.jump_list.pop().unwrap(); + self.add_quad(Quadruple { + operator: Operator::Goto, + op_1: None, + op_2: None, + res: Some(goto_res), + }); + Ok(self.fill_goto_index(index)) + } + AstNodeKind::For { + assignment, + expr, + statements, + } => { + let name = String::from(*assignment.clone()); + self.parse_function(*assignment)?; + self.jump_list.push(self.quad_list.len()); + let (res_address, _) = self.assert_expr_type(*expr, Types::BOOL)?; + self.add_goto(Operator::GotoF, Some(res_address)); + self.parse_body(statements)?; + let result = self.get_variable_name_address(name, node_clone.clone()); + if let Err(error) = result { + return Err(vec![error]); + } + let (var_address, var_type) = result.unwrap(); + self.assert_type(var_type, Types::INT, node_clone)?; + self.add_quad(Quadruple { + operator: Operator::Inc, + op_1: None, + op_2: None, + res: Some(var_address), + }); + let index = self.jump_list.pop().unwrap(); + let goto_res = self.jump_list.pop().unwrap(); + self.add_quad(Quadruple { + operator: Operator::Goto, + op_1: None, + op_2: None, + res: Some(goto_res), + }); + Ok(self.fill_goto_index(index)) + } + _ => unreachable!("{:?}", node.kind), + } + } + + pub fn parse<'a>(&mut self, node: AstNode<'a>) -> Results<'a, ()> { + match node.kind { + AstNodeKind::Main { body, .. } => { + self.function_name = "main".to_owned(); + Ok(self.parse_body(body)?) + } + AstNodeKind::Function { .. } => todo!(), + _ => unreachable!(), + } + } +} + +impl fmt::Debug for QuadrupleManager<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let value: String = self + .quad_list + .clone() + .into_iter() + .enumerate() + .map(|(i, quad)| format!("{} - {:?}\n", i, quad)) + .collect(); + write!(f, "{value}") + } +} diff --git a/src/tests.rs b/src/tests.rs index ec0e64b..4063bd0 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,53 +1,46 @@ -use super::{parse, DirFunc}; +use super::{parse, parse_ast}; -#[test] -fn use_of_undeclared_variable() { - let filename = "examples/invalid/undeclared-variable.ra"; +fn parse_ast_has_error(filename: &str) { let program = std::fs::read_to_string(filename).expect(filename); let debug = true; let ast_response = parse(&program, debug); assert!(ast_response.is_ok()); let ast = ast_response.unwrap(); - let mut dir_func = DirFunc::new(); - let build_res = dir_func.build_dir_func(ast); - assert!(build_res.is_err()); + assert!(parse_ast(ast, debug).is_err()); +} + +#[test] +fn use_of_undeclared_variable() { + let filename = "examples/invalid/undeclared-variable.ra"; + parse_ast_has_error(filename); +} + +#[test] +fn use_of_undeclared_variable_if() { + let filename = "examples/invalid/undeclared-variable-if.ra"; + parse_ast_has_error(filename); +} + +#[test] +fn use_of_undeclared_variable_while() { + let filename = "examples/invalid/undeclared-variable-while.ra"; + parse_ast_has_error(filename); } #[test] fn redefinition_of_function() { let filename = "examples/invalid/redefined-function.ra"; - let program = std::fs::read_to_string(filename).expect(filename); - let debug = true; - let ast_response = parse(&program, debug); - assert!(ast_response.is_ok()); - let ast = ast_response.unwrap(); - let mut dir_func = DirFunc::new(); - let build_res = dir_func.build_dir_func(ast); - assert!(build_res.is_err()); + parse_ast_has_error(filename); } #[test] fn redefine_variable_type() { let filename = "examples/invalid/redefine-variable-type.ra"; - let program = std::fs::read_to_string(filename).expect(filename); - let debug = true; - let ast_response = parse(&program, debug); - assert!(ast_response.is_ok()); - let ast = ast_response.unwrap(); - let mut dir_func = DirFunc::new(); - let build_res = dir_func.build_dir_func(ast); - assert!(build_res.is_err()); + parse_ast_has_error(filename); } #[test] fn invalid_cast() { let filename = "examples/invalid/invalid-cast.ra"; - let program = std::fs::read_to_string(filename).expect(filename); - let debug = true; - let ast_response = parse(&program, debug); - assert!(ast_response.is_ok()); - let ast = ast_response.unwrap(); - let mut dir_func = DirFunc::new(); - let build_res = dir_func.build_dir_func(ast); - assert!(build_res.is_err()); + parse_ast_has_error(filename); }