From 66c24ac075a55c97a20973ef9ee1eafba53d7529 Mon Sep 17 00:00:00 2001 From: Ramesh Poudel Date: Wed, 7 Feb 2024 23:45:27 +0545 Subject: [PATCH] improving compiler by writing some noob tests --- src/ast.rs | 1 + src/parser.rs | 50 ++++++++++++++++++++++++++++++++++++++- src/symtable.rs | 61 ++++++++++++++++++++++++++++++++++++++++++------ src/tokenizer.rs | 17 ++++++++++---- 4 files changed, 117 insertions(+), 12 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index c79e7ff..32cb243 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -246,6 +246,7 @@ impl ASTTraverser { value_containing_reg } + // Refer to this page for explanation on '@PAGE' and '@PAGEOFF': https://stackoverflow.com/questions/65351533/apple-clang12-llvm-unknown-aarch64-fixup-kind fn gen_load_reg_into_gid(&mut self, reg: usize, id: &LitType) -> usize { let reg_name: String = self.reg_manager.borrow().name(reg); let mut offset: usize = 0; diff --git a/src/parser.rs b/src/parser.rs index 0124283..26722bf 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -177,6 +177,9 @@ impl<'a> Parser<'a> { cond_ast } + /* + TODO: Check type of the variable before assigning + */ fn parse_assignment_stmt(&mut self) -> Option { let id_token: Token = self.token_match(TokenKind::T_IDENTIFIER).clone(); let _id_index_symt: usize = self.sym_table.borrow().find_symbol(&id_token.lexeme); @@ -275,7 +278,7 @@ impl<'a> Parser<'a> { (left.clone(), right, rt) } }; - return Some(ASTNode::new(ASTNodeKind::from_token_kind(current_token_kind), Some(_left), Some(_right), None, _rtype)); + return Some(ASTNode::new(ASTNodeKind::from_token_kind(current_token_kind), Some(_left), Some(_right), Some(LitType::I32(0)), _rtype)); } else { panic!("Something unexpected happended with this token: '{:?}'", self.current_token); } @@ -299,6 +302,12 @@ impl<'a> Parser<'a> { let symbol: Symbol = self.sym_table.borrow().get_symbol(id_index).clone(); Some(ASTNode::make_leaf(ASTNodeKind::AST_IDENT, LitType::I32(id_index as i32), symbol.lit_type)) }, + TokenKind::T_LPAREN => { // group expression: e.g: (a * (b + c))) + let group_expr: Option = self.parse_equality(); + // Group expression terminates with ')'. Match and ignore ')'. + self.token_match(TokenKind::T_RPAREN); + group_expr + }, _ => { println!("{:?}", current_token); panic!("Unrecognized primitive type: {:?}", self.current_token); @@ -335,6 +344,22 @@ impl<'a> Parser<'a> { mod tests { use super::*; + #[test] + fn test_group_expression_tree_structure() { + let mut tokener: Tokenizer = Tokenizer::new("(5 + (3 * 4))"); + let tokens: Vec = tokener.start_scan(); + let sym_table: Rc> = Rc::new(RefCell::new(Symtable::new())); + let mut p: Parser = Parser::new(&tokens, Rc::clone(&sym_table)); + let result: Option = p.parse_equality(); + matches!(result, Some(_)); + let upvalue: ASTNode = result.unwrap(); + let left_tree: &ASTNode = (*upvalue.left).as_ref().unwrap(); + let right_tree: &ASTNode = (*upvalue.right).as_ref().unwrap(); + assert_eq!(upvalue.operation, ASTNodeKind::AST_ADD); + assert_eq!(left_tree.operation, ASTNodeKind::AST_INTLIT); + assert_eq!(right_tree.operation, ASTNodeKind::AST_MULTIPLY); + } + // test addition operation #[test] fn test_depth_one_bin_tree() { @@ -346,4 +371,27 @@ mod tests { matches!(result, Some(_)); assert_eq!(result.unwrap().operation, ASTNodeKind::AST_ADD); } + + // test if-else block + #[test] + fn test_if_else_statement_block() { + let mut tokener: Tokenizer = Tokenizer::new("if (4 > 5) { global int a; } else { global int b; }"); + let tokens: Vec = tokener.start_scan(); + let sym_table: Rc> = Rc::new(RefCell::new(Symtable::new())); + let mut p: Parser = Parser::new(&tokens, Rc::clone(&sym_table)); + let result: Option = p.parse_if_stmt(); + matches!(result, Some(_)); + let upvalue: &ASTNode = result.as_ref().unwrap(); + // Global variable declaration statements produce None as result. + // So, both 'mid (if)' and 'right (else)' has to be None types + matches!(*upvalue.mid, None); + matches!(*upvalue.right, None); + assert_eq!(upvalue.operation, ASTNodeKind::AST_IF); // main AST node is of AST_IF type + assert_eq!((*upvalue.left).as_ref().unwrap().operation, ASTNodeKind::AST_GTHAN); + } + + #[test] + fn test_while_statement_block() { + + } } \ No newline at end of file diff --git a/src/symtable.rs b/src/symtable.rs index b3f3f63..2171814 100644 --- a/src/symtable.rs +++ b/src/symtable.rs @@ -26,7 +26,7 @@ use crate::enums::*; use crate::types::*; use std::slice::Iter; -#[derive(Clone)] +#[derive(Clone, PartialEq, Debug)] pub struct Symbol { pub name: String, pub lit_type: LitType, // What kind of value this symbol is @@ -66,10 +66,8 @@ impl Symtable { } fn next(&mut self) -> usize { + assert!(self.counter < NSYMBOLS); self.counter += 1; - if self.counter >= NSYMBOLS { - panic!("too many global symbols"); - } self.counter } @@ -84,9 +82,58 @@ impl Symtable { } pub fn get_symbol(&self, idx: usize) -> &Symbol { - if idx >= self.syms.len() { - panic!("index '{}' out of bounds for range '{}'", idx, self.syms.len()); - } + assert!(self.counter < NSYMBOLS); self.syms.get(idx).unwrap() } + + pub fn remove_symbol(&mut self, index: usize) -> Symbol { + assert!(self.counter < NSYMBOLS); + self.syms.remove(index) + } +} + +#[cfg(test)] +mod tests { + use super::{Symbol, SymbolType, Symtable, NSYMBOLS}; + + #[test] + fn test_symbol_addition() { + let mut table: Symtable = Symtable::new(); + assert_eq!(table.add_symbol(Symbol::new(String::from("number"), super::LitType::I32(0), SymbolType::Variable)), 0); + assert_eq!(table.syms.len(), 1); + assert_eq!(table.add_symbol(Symbol::new(String::from("number2"), super::LitType::I32(0), SymbolType::Variable)), 1); + assert_eq!(table.add_symbol(Symbol::new(String::from("number3"), super::LitType::I32(0), SymbolType::Variable)), 2); + assert_eq!(table.add_symbol(Symbol::new(String::from("number4"), super::LitType::I32(0), SymbolType::Variable)), 3); + assert_eq!(table.add_symbol(Symbol::new(String::from("number5"), super::LitType::I32(0), SymbolType::Variable)), 4); + assert_eq!(table.add_symbol(Symbol::new(String::from("number6"), super::LitType::I32(0), SymbolType::Variable)), 5); + } + + // This test insures that no more than 1024 symbols are defined in program. + #[test] + #[should_panic(expected="assertion failed")] + fn test_more_than_1024_symbols_creates_panic_situation() { + let mut table: Symtable = Symtable::new(); + table.counter = NSYMBOLS; + table.add_symbol(Symbol::new(String::from("number"), super::LitType::I32(0), SymbolType::Variable)); + } + + #[test] + fn test_find_symbol_index_from_it_name() { + let mut table: Symtable = Symtable::new(); + table.add_symbol(Symbol::new(String::from("number2"), super::LitType::I32(0), SymbolType::Variable)); + table.add_symbol(Symbol::new(String::from("number3"), super::LitType::I32(0), SymbolType::Variable)); + table.add_symbol(Symbol::new(String::from("number4"), super::LitType::I32(0), SymbolType::Variable)); + assert_eq!(table.find_symbol("number2"), 0); + assert_eq!(table.find_symbol("number3"), 1); + assert_eq!(table.find_symbol("number4"), 2); + } + + #[test] + fn test_symbol_removal() { + let mut table: Symtable = Symtable::new(); + table.add_symbol(Symbol::new(String::from("number2"), super::LitType::I32(0), SymbolType::Variable)); + table.add_symbol(Symbol::new(String::from("number3"), super::LitType::I32(0), SymbolType::Variable)); + table.add_symbol(Symbol::new(String::from("number4"), super::LitType::I32(0), SymbolType::Variable)); + assert_eq!(table.remove_symbol(0), Symbol::new(String::from("number2"), crate::symtable::LitType::I32(0), SymbolType::Variable)); + } } \ No newline at end of file diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 554b85f..7f94aee 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -426,7 +426,7 @@ impl Tokenizer { } else { let _value: i64 = number.parse::().unwrap(); token.kind = if (0..256).contains(&_value) { TokenKind::T_CHAR } - else if (0..std::i64::MAX).contains(&_value) { TokenKind::T_LONG_NUM } + else if ((std::i32::MAX as i64)..std::i64::MAX).contains(&_value) { TokenKind::T_LONG_NUM } else { TokenKind::T_INT_NUM } } token.lexeme = String::from(number); @@ -554,15 +554,24 @@ mod tests { fn test_empty_source() { let mut tok: Tokenizer = Tokenizer::new(""); let tokens: Vec = tok.start_scan(); - assert_eq!(tokens.len(), 1); // only EOF is present - assert_eq!(tokens[0].kind, TokenKind::T_EOF); // only EOF is present + assert_eq!(tokens.len(), 1); // only T_EOF is present + assert_eq!(tokens[0].kind, TokenKind::T_EOF); // only T_EOF is present } #[test] fn test_only_whitespace_source() { let mut tok: Tokenizer = Tokenizer::new(" "); let tokens: Vec = tok.start_scan(); - assert_eq!(tokens.len(), 1); // only EOF is present + assert_eq!(tokens.len(), 1); // only T_EOF is present assert_eq!(tokens[0].kind, TokenKind::T_EOF); // only EOF is present } + + #[test] + fn test_while_if_else_statement() { + let mut tok: Tokenizer = Tokenizer::new("if (4 > 5) { } else { }"); + let tokens: Vec = tok.start_scan(); + assert_eq!(tokens.len(), 12); // including T_EOF + assert_eq!(tokens[0].kind, TokenKind::KW_IF); + assert_eq!(tokens[8].kind, TokenKind::KW_ELSE); + } } \ No newline at end of file