diff --git a/src/codegen/function_call.rs b/src/codegen/function_call.rs index ee48731..dd01692 100644 --- a/src/codegen/function_call.rs +++ b/src/codegen/function_call.rs @@ -4,7 +4,13 @@ use super::Transpilable; impl Transpilable for FunctionCall { fn transpile(&self) -> String { - let parameters = &self.arguments.arguments.iter().map(|expr| expr.transpile()).collect::>().join(", "); + let parameters = &self + .arguments + .arguments + .iter() + .map(|expr| expr.transpile()) + .collect::>() + .join(", "); format!("{}({})", self.function.transpile(), parameters) } diff --git a/src/main.rs b/src/main.rs index 534b938..9e3b281 100755 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,8 @@ mod file; mod syntax; // Module to handle syntactic analysis mod lexic; +// Module to handle semantic analysis +mod semantic; // Transforms an AST to JS mod codegen; mod utils; diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs new file mode 100644 index 0000000..8956ea2 --- /dev/null +++ b/src/semantic/mod.rs @@ -0,0 +1,52 @@ +mod symbol_table; + +use symbol_table::{SymbolEntry, SymbolTable}; + +// What to do? +// 1. Create a mutable symbol table +// 2. Walk the AST +// 3. Add the symbols declared to the symbol table, annotating them with their type +// 4. Check if the symbols used are declared + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_1() { + let mut global_scope = SymbolTable::new(); + let main_function = SymbolEntry::new_function(vec![], String::from("Unit")); + + global_scope.insert("main".into(), main_function); + + assert!(global_scope.test(&"main".into())); + } + + #[test] + fn test_2() { + let mut global_scope = SymbolTable::new(); + let main_function = SymbolEntry::new_function(vec![], String::from("Unit")); + global_scope.insert("main".into(), main_function); + global_scope.insert("db_url".into(), SymbolEntry::Variable("String".into())); + + let add_function = + SymbolEntry::new_function(vec!["Int".into(), "Int".into()], "Int".into()); + global_scope.insert("add".into(), add_function); + + let mut main_function_scope = SymbolTable::new_from_parent(&global_scope); + + main_function_scope.insert("message".into(), SymbolEntry::Variable("String".into())); + + assert!(main_function_scope.test(&"message".into())); + assert!(main_function_scope.test(&"db_url".into())); + assert_eq!(main_function_scope.test(&"non_existant".into()), false); + + let mut add_function_scope = SymbolTable::new_from_parent(&global_scope); + add_function_scope.insert("a".into(), SymbolEntry::Variable("Int".into())); + add_function_scope.insert("b".into(), SymbolEntry::Variable("Int".into())); + + assert!(add_function_scope.test(&"a".into())); + + global_scope.insert("test".into(), SymbolEntry::Variable("Int".into())); + } +} diff --git a/src/semantic/symbol_table.rs b/src/semantic/symbol_table.rs new file mode 100644 index 0000000..779169b --- /dev/null +++ b/src/semantic/symbol_table.rs @@ -0,0 +1,64 @@ +use std::{collections::HashMap, rc::Rc, cell::RefCell}; + +// struct for a symbol table +pub struct SymbolTable<'a> { + // the parent scope + parent: Option>>>, + // the current scope + scope: HashMap, +} + +pub enum SymbolEntry { + // Just a Datatype + Variable(String), + // Contains: parameters, return type + Function(Vec, String), +} + +impl SymbolTable<'_> { + /// Creates a new, global symbol table + pub fn new<'a>() -> SymbolTable<'a> { + SymbolTable { + parent: None, + scope: HashMap::new(), + } + } + + /// Creates a new symbol table with a parent + pub fn new_from_parent<'a>(parent: &'a SymbolTable<'a>) -> SymbolTable<'a> { + SymbolTable { + parent: Some(Rc::new(RefCell::new(parent))), + scope: HashMap::new(), + } + } + + /// Inserts a new symbol into the current scope + pub fn insert(&mut self, key: String, value: SymbolEntry) { + self.scope.insert(key, value); + } + + /// Tests if a symbol is declared in the current or parent scopes + pub fn test(&self, key: &String) -> bool { + if self.scope.contains_key(key) { + return true; + } + + match &self.parent { + Some(parent) => { + let parent = parent.borrow(); + parent.test(key) + }, + None => false, + } + } +} + +impl SymbolEntry { + pub fn new_variable(datatype: String) -> SymbolEntry { + SymbolEntry::Variable(datatype) + } + + pub fn new_function(parameters: Vec, return_type: String) -> SymbolEntry { + SymbolEntry::Function(parameters, return_type) + } +} diff --git a/src/syntax/ast/functions.rs b/src/syntax/ast/functions.rs index f864f47..43477bc 100644 --- a/src/syntax/ast/functions.rs +++ b/src/syntax/ast/functions.rs @@ -10,4 +10,3 @@ pub struct FunctionCall { pub struct ArgumentsList { pub arguments: Vec, } - diff --git a/src/syntax/block.rs b/src/syntax/block.rs index 31c3e91..8209e5a 100644 --- a/src/syntax/block.rs +++ b/src/syntax/block.rs @@ -114,7 +114,7 @@ mod tests { ParseResult::Ok(p, _) => p, _ => { panic!("Expected a block, got: {:?}\n\n{:?}", block, tokens) - }, + } }; assert_eq!(block.statements.len(), 1); diff --git a/src/syntax/expression/function_call_expr.rs b/src/syntax/expression/function_call_expr.rs index a1eec04..0ac781c 100644 --- a/src/syntax/expression/function_call_expr.rs +++ b/src/syntax/expression/function_call_expr.rs @@ -1,10 +1,7 @@ use crate::{ lexic::token::Token, syntax::{ - ast::{ - functions::{ArgumentsList, FunctionCall}, - Expression, - }, + ast::{functions::FunctionCall, Expression}, functions::arguments_list, ParseResult, }, diff --git a/src/syntax/expression/mod.rs b/src/syntax/expression/mod.rs index faecc77..6fb493a 100644 --- a/src/syntax/expression/mod.rs +++ b/src/syntax/expression/mod.rs @@ -14,8 +14,5 @@ pub fn try_parse(tokens: &Vec, pos: usize) -> ParseResult return equality::try_parse(tokens, pos); } - #[cfg(test)] -mod tests { - -} +mod tests {} diff --git a/src/syntax/expression/primary.rs b/src/syntax/expression/primary.rs index 1a359e9..bfa567f 100644 --- a/src/syntax/expression/primary.rs +++ b/src/syntax/expression/primary.rs @@ -1,8 +1,8 @@ +use super::super::utils::Tokenizer; use crate::{ lexic::token::{Token, TokenType}, syntax::{ast::Expression, ParseResult}, }; -use super::super::utils::Tokenizer; /// This grammar may not be up to date. Refer to the spec for the latest grammar. /// @@ -12,12 +12,14 @@ use super::super::utils::Tokenizer; pub fn try_parse(tokens: &Vec, pos: usize) -> ParseResult { match tokens.get_significant(pos) { Some((token, token_pos)) => match token.token_type { - TokenType::Number => { - ParseResult::Ok(Expression::Number(Box::new(token.value.clone())), token_pos + 1) - } - TokenType::String => { - ParseResult::Ok(Expression::String(Box::new(token.value.clone())), token_pos + 1) - } + TokenType::Number => ParseResult::Ok( + Expression::Number(Box::new(token.value.clone())), + token_pos + 1, + ), + TokenType::String => ParseResult::Ok( + Expression::String(Box::new(token.value.clone())), + token_pos + 1, + ), TokenType::Identifier if token.value == "true" || token.value == "false" => { ParseResult::Ok(Expression::Boolean(token.value == "true"), token_pos + 1) } diff --git a/src/syntax/functions/arguments_list.rs b/src/syntax/functions/arguments_list.rs index 51d270b..77f67b1 100644 --- a/src/syntax/functions/arguments_list.rs +++ b/src/syntax/functions/arguments_list.rs @@ -79,8 +79,6 @@ pub fn try_parse<'a>(tokens: &'a Vec, pos: usize) -> ParseResult(tokens: &'a Vec, pos: usize) -> ParseResult { let mut current_pos = pos; diff --git a/src/syntax/functions/mod.rs b/src/syntax/functions/mod.rs index f4207d0..6bdfe94 100644 --- a/src/syntax/functions/mod.rs +++ b/src/syntax/functions/mod.rs @@ -1,3 +1,5 @@ pub mod arguments_list; pub mod function_declaration; +pub mod params_list; + diff --git a/src/syntax/params_list.rs b/src/syntax/functions/params_list.rs similarity index 99% rename from src/syntax/params_list.rs rename to src/syntax/functions/params_list.rs index 43c8804..a565cba 100644 --- a/src/syntax/params_list.rs +++ b/src/syntax/functions/params_list.rs @@ -4,7 +4,7 @@ use crate::{ syntax::utils::parse_token_type, }; -use super::{ +use super::super::{ ast::{Parameter, ParamsList}, utils, ParseResult, }; diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index c52406c..958f84c 100755 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -4,7 +4,6 @@ mod binding; mod block; mod expression; mod functions; -mod params_list; mod statement; mod utils;