Compare commits

...

2 Commits

Author SHA1 Message Date
e1a0afba36 Check if functions are declared twice 2024-02-03 20:21:48 -05:00
af0eb6414a Small refactors 2024-02-03 19:51:33 -05:00
7 changed files with 77 additions and 67 deletions

View File

@ -23,8 +23,9 @@
- [x] Parse function call parameters - [x] Parse function call parameters
- [x] Codegen function call parameters - [x] Codegen function call parameters
- [x] Parse function declaration arguments (Type id) - [x] Parse function declaration arguments (Type id)
- [ ] Begin work on semantic analysis - [x] Begin work on semantic analysis
- [ ] Symbol table - [x] Minimal symbol table
- [x] Check duplicate function declarations
- [ ] Typecheck bindings - [ ] Typecheck bindings
- [ ] Typecheck functions - [ ] Typecheck functions
- [ ] Transform simple THP expression into PHP statements - [ ] Transform simple THP expression into PHP statements

View File

@ -19,23 +19,3 @@ trait Transpilable {
pub fn codegen<'a>(ast: &'a ModuleAST) -> String { pub fn codegen<'a>(ast: &'a ModuleAST) -> String {
ast.transpile() ast.transpile()
} }
#[cfg(test)]
mod tests {
use crate::{lexic, syntax};
use super::*;
#[test]
fn should_codegen_1() {
/*
let input = String::from("val id = 322");
let tokens = lexic::get_tokens(&input).unwrap();
let ast = syntax::construct_ast(&tokens).unwrap();
let out_str = codegen(&ast);
assert_eq!("$id = 322;", out_str);
*/
}
}

View File

@ -116,13 +116,9 @@ fn get_line_number(chars: &Vec<char>, target_pos: usize) -> usize {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{ use crate::{error_handling::MistiError, lexic::get_tokens, syntax::construct_ast};
error_handling::{MistiError, PrintableError},
lexic::get_tokens,
syntax::construct_ast,
};
fn get_error_data(input: String) -> (Vec<char>, MistiError) { fn _get_error_data(input: String) -> (Vec<char>, MistiError) {
let tokens = get_tokens(&input).unwrap(); let tokens = get_tokens(&input).unwrap();
let error_holder = construct_ast(&tokens); let error_holder = construct_ast(&tokens);
@ -139,38 +135,6 @@ mod tests {
} }
} }
#[test]
fn should_show_an_error_for_missing_binding_name() {
let (chars, error) = get_error_data(String::from("val"));
let actual_err = error.get_error_str(&chars);
// TODO: Write a better error message (something that explains why it failed)
let expected_str = format!(
"\n{}\n{}\n\n{}\n{}",
"val",
"^^^",
"Syntax error at pos 0:",
"There should be an identifier after a `val` token"
);
// assert_eq!(expected_str, actual_err);
}
#[test]
fn should_show_an_error_for_missing_equal_operator() {
let (chars, error) = get_error_data(String::from("val name"));
let actual_err = error.get_error_str(&chars);
// TODO: Write a better error message (something that explains why it failed)
let expected_str = format!(
"\n{}\n{}\n\n{}\n{}",
"val name",
" ^^^^",
"Syntax error at pos 4:",
"There should be an equal sign `=` after the identifier"
);
// assert_eq!(expected_str, actual_err);
}
#[test] #[test]
fn should_get_line() { fn should_get_line() {
let input: Vec<char> = String::from("\n\nval number == 50\n\n") let input: Vec<char> = String::from("\n\nval number == 50\n\n")

View File

@ -62,7 +62,16 @@ fn build_ast(input: &String, tokens: Vec<Token>) -> String {
let ast = syntax::construct_ast(&tokens); let ast = syntax::construct_ast(&tokens);
match ast { match ast {
Ok(ast) => codegen::codegen(&ast), Ok(ast) => {
match crate::semantic::check_semantics(&ast) {
Ok(_) => {}
Err(reason) => {
panic!("{}", reason)
}
};
codegen::codegen(&ast)
}
Err(reason) => { Err(reason) => {
let chars: Vec<char> = input.chars().into_iter().collect(); let chars: Vec<char> = input.chars().into_iter().collect();
panic!("{}", reason.get_error_str(&chars)) panic!("{}", reason.get_error_str(&chars))

View File

@ -30,6 +30,15 @@ fn build_ast(input: &String, tokens: Vec<Token>) {
match ast { match ast {
Ok(ast) => { Ok(ast) => {
let res1 = crate::semantic::check_semantics(&ast);
match res1 {
Ok(_) => {}
Err(reason) => {
eprintln!("{}", reason);
return;
}
}
let js_code = codegen::codegen(&ast); let js_code = codegen::codegen(&ast);
println!("{}", js_code) println!("{}", js_code)
} }

39
src/semantic/impls.rs Normal file
View File

@ -0,0 +1,39 @@
use crate::syntax::ast::{ModuleAST, TopLevelDeclaration};
use super::symbol_table::SymbolTable;
pub trait SemanticCheck {
fn check_semantics(&self, scope: &SymbolTable) -> Result<(), String>;
}
impl SemanticCheck for ModuleAST {
fn check_semantics(&self, scope: &SymbolTable) -> Result<(), String> {
for declaration in &self.declarations {
declaration.check_semantics(scope)?;
}
Ok(())
}
}
impl SemanticCheck for TopLevelDeclaration {
fn check_semantics(&self, scope: &SymbolTable) -> Result<(), String> {
match self {
TopLevelDeclaration::Binding(_) => Err("Binding not implemented".into()),
TopLevelDeclaration::FunctionDeclaration(function) => {
let function_name = function.identifier.as_ref().clone();
if scope.test(&function_name) {
return Err(format!("Function {} already defined", function_name));
}
scope.insert(
function_name,
super::symbol_table::SymbolEntry::Function(vec![], "Unit".into()),
);
Ok(())
}
}
}
}

View File

@ -1,6 +1,9 @@
use crate::syntax::ast::ModuleAST;
mod impls;
mod symbol_table; mod symbol_table;
use symbol_table::{SymbolEntry, SymbolTable}; use impls::SemanticCheck;
// What to do? // What to do?
// 1. Create a mutable symbol table // 1. Create a mutable symbol table
@ -8,15 +11,20 @@ use symbol_table::{SymbolEntry, SymbolTable};
// 3. Add the symbols declared to the symbol table, annotating them with their type // 3. Add the symbols declared to the symbol table, annotating them with their type
// 4. Check if the symbols used are declared // 4. Check if the symbols used are declared
pub fn check_semantics(ast: &ModuleAST) -> Result<(), String> {
// For now there's only support for a single file
let global_scope = symbol_table::SymbolTable::new();
ast.check_semantics(&global_scope)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::{borrow::BorrowMut, rc::Rc}; use super::symbol_table::{SymbolEntry, SymbolTable};
use super::*;
#[test] #[test]
fn test_1() { fn test_1() {
let mut global_scope = SymbolTable::new(); let global_scope = SymbolTable::new();
let main_function = SymbolEntry::new_function(vec![], String::from("Unit")); let main_function = SymbolEntry::new_function(vec![], String::from("Unit"));
global_scope.insert("main".into(), main_function); global_scope.insert("main".into(), main_function);
@ -44,7 +52,7 @@ mod tests {
assert!(main_function_scope.test(&"db_url".into())); assert!(main_function_scope.test(&"db_url".into()));
assert_eq!(main_function_scope.test(&"non_existant".into()), false); assert_eq!(main_function_scope.test(&"non_existant".into()), false);
let mut add_function_scope = SymbolTable::new_from_parent(&global_scope); let add_function_scope = SymbolTable::new_from_parent(&global_scope);
add_function_scope.insert("a".into(), SymbolEntry::Variable("Int".into())); add_function_scope.insert("a".into(), SymbolEntry::Variable("Int".into()));
add_function_scope.insert("b".into(), SymbolEntry::Variable("Int".into())); add_function_scope.insert("b".into(), SymbolEntry::Variable("Int".into()));