Compare commits
No commits in common. "e1a0afba36444e634760487de0e541d783c14f25" and "83c0c59ae97ea9a4cf5d0ef88e867408c1074cdd" have entirely different histories.
e1a0afba36
...
83c0c59ae9
@ -23,9 +23,8 @@
|
|||||||
- [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)
|
||||||
- [x] Begin work on semantic analysis
|
- [ ] Begin work on semantic analysis
|
||||||
- [x] Minimal symbol table
|
- [ ] 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
|
||||||
|
@ -19,3 +19,23 @@ 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);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -116,9 +116,13 @@ 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::{error_handling::MistiError, lexic::get_tokens, syntax::construct_ast};
|
use crate::{
|
||||||
|
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);
|
||||||
|
|
||||||
@ -135,6 +139,38 @@ 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")
|
||||||
|
@ -62,16 +62,7 @@ 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) => {
|
Ok(ast) => codegen::codegen(&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))
|
||||||
|
@ -30,15 +30,6 @@ 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)
|
||||||
}
|
}
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
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(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +1,6 @@
|
|||||||
use crate::syntax::ast::ModuleAST;
|
|
||||||
|
|
||||||
mod impls;
|
|
||||||
mod symbol_table;
|
mod symbol_table;
|
||||||
|
|
||||||
use impls::SemanticCheck;
|
use symbol_table::{SymbolEntry, SymbolTable};
|
||||||
|
|
||||||
// What to do?
|
// What to do?
|
||||||
// 1. Create a mutable symbol table
|
// 1. Create a mutable symbol table
|
||||||
@ -11,20 +8,15 @@ use impls::SemanticCheck;
|
|||||||
// 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 super::symbol_table::{SymbolEntry, SymbolTable};
|
use std::{borrow::BorrowMut, rc::Rc};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_1() {
|
fn test_1() {
|
||||||
let global_scope = SymbolTable::new();
|
let mut 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);
|
||||||
@ -52,7 +44,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 add_function_scope = SymbolTable::new_from_parent(&global_scope);
|
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("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()));
|
||||||
|
Loading…
Reference in New Issue
Block a user