Compare commits
No commits in common. "59894a1b643399718e7a82d06e2f8d9cd4bad481" and "46d9d04c755f76622c288b5f34a2be736e11865b" have entirely different histories.
59894a1b64
...
46d9d04c75
@ -43,7 +43,7 @@
|
|||||||
- [x] Parse function declaration arguments (`Type id`)
|
- [x] Parse function declaration arguments (`Type id`)
|
||||||
- [x] Parse function return datatype (`fun f() -> Type`)
|
- [x] Parse function return datatype (`fun f() -> Type`)
|
||||||
- [x] Return parsing to variables to var/val
|
- [x] Return parsing to variables to var/val
|
||||||
- [x] Write tests
|
- [ ] Write tests
|
||||||
|
|
||||||
|
|
||||||
## v0.0.10
|
## v0.0.10
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
error_handling::{semantic_error::SemanticError, MistiError},
|
error_handling::{semantic_error::SemanticError, MistiError},
|
||||||
semantic::{impls::SemanticCheck, symbol_table::SymbolEntry, types::Typed},
|
semantic::{impls::SemanticCheck, symbol_table::SymbolEntry},
|
||||||
syntax::ast::var_binding::Binding,
|
syntax::ast::var_binding::Binding,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -26,8 +26,8 @@ impl SemanticCheck for Binding<'_> {
|
|||||||
return Err(MistiError::Semantic(error));
|
return Err(MistiError::Semantic(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
// This gets the datatype of the assigned expression,
|
todo!("");
|
||||||
// to compare it later with the declared datatype.
|
/*
|
||||||
let expression_datatype = self.expression.get_type();
|
let expression_datatype = self.expression.get_type();
|
||||||
|
|
||||||
let datatype = match self.datatype {
|
let datatype = match self.datatype {
|
||||||
@ -53,5 +53,6 @@ impl SemanticCheck for Binding<'_> {
|
|||||||
scope.insert(binding_name.clone(), SymbolEntry::new_variable(datatype));
|
scope.insert(binding_name.clone(), SymbolEntry::new_variable(datatype));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
pub mod binding;
|
pub mod binding;
|
||||||
pub mod function_declaration;
|
pub mod function_declaration;
|
||||||
pub mod top_level_declaration;
|
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
use crate::{semantic::impls::SemanticCheck, syntax::ast::TopLevelDeclaration};
|
|
||||||
|
|
||||||
impl SemanticCheck for TopLevelDeclaration<'_> {
|
|
||||||
fn check_semantics(&self, scope: &crate::semantic::symbol_table::SymbolTable) -> Result<(), crate::error_handling::MistiError> {
|
|
||||||
match self {
|
|
||||||
TopLevelDeclaration::Binding(binding) => binding.check_semantics(scope),
|
|
||||||
TopLevelDeclaration::FunctionDeclaration(function) => function.check_semantics(scope),
|
|
||||||
_ => panic!("Not implemented"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
|||||||
use crate::{error_handling::MistiError, syntax::ast::ModuleAST};
|
use crate::{
|
||||||
|
error_handling::semantic_error::SemanticError,
|
||||||
|
error_handling::MistiError,
|
||||||
|
syntax::ast::{ModuleAST, TopLevelDeclaration},
|
||||||
|
};
|
||||||
|
|
||||||
use super::symbol_table::SymbolTable;
|
use super::symbol_table::{SymbolEntry, SymbolTable};
|
||||||
|
|
||||||
/// Allows this type to have it's semantics checked.
|
|
||||||
pub trait SemanticCheck {
|
pub trait SemanticCheck {
|
||||||
fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError>;
|
fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError>;
|
||||||
}
|
}
|
||||||
@ -17,3 +20,74 @@ impl SemanticCheck for ModuleAST<'_> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SemanticCheck for TopLevelDeclaration<'_> {
|
||||||
|
fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError> {
|
||||||
|
match self {
|
||||||
|
TopLevelDeclaration::Binding(binding) => {
|
||||||
|
let binding_name = &binding.identifier.value;
|
||||||
|
|
||||||
|
if scope.test(binding_name) {
|
||||||
|
let error = SemanticError {
|
||||||
|
error_start: binding.identifier.position,
|
||||||
|
error_end: binding.identifier.get_end_position(),
|
||||||
|
reason: format!(
|
||||||
|
"Duplicated: A symbol with name {} was already defined",
|
||||||
|
binding_name
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
return Err(MistiError::Semantic(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
let datatype = match binding.datatype {
|
||||||
|
Some(t) => t,
|
||||||
|
None => {
|
||||||
|
let error = SemanticError {
|
||||||
|
error_start: binding.identifier.position,
|
||||||
|
error_end: binding.identifier.get_end_position(),
|
||||||
|
reason: format!(
|
||||||
|
"The variable `{}` didn't define a datatype. Datatype inference is not implemented.",
|
||||||
|
binding_name
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
return Err(MistiError::Semantic(error));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.insert(
|
||||||
|
binding_name.clone(),
|
||||||
|
SymbolEntry::new_variable(datatype.value.clone()),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
TopLevelDeclaration::FunctionDeclaration(function) => {
|
||||||
|
let function_name = function.identifier.value.clone();
|
||||||
|
|
||||||
|
// Check that the function is not already defined
|
||||||
|
if scope.test(&function_name) {
|
||||||
|
let error = SemanticError {
|
||||||
|
error_start: function.identifier.position,
|
||||||
|
error_end: function.identifier.get_end_position(),
|
||||||
|
reason: format!(
|
||||||
|
"Duplicated: A symbol with name {} was already defined",
|
||||||
|
function_name
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
return Err(MistiError::Semantic(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.insert(
|
||||||
|
function_name,
|
||||||
|
SymbolEntry::new_function(vec![], "Unit".into()),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => panic!("Not implemented"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,7 +3,6 @@ use crate::{error_handling::MistiError, syntax::ast::ModuleAST};
|
|||||||
mod impls;
|
mod impls;
|
||||||
mod symbol_table;
|
mod symbol_table;
|
||||||
mod checks;
|
mod checks;
|
||||||
mod types;
|
|
||||||
|
|
||||||
use impls::SemanticCheck;
|
use impls::SemanticCheck;
|
||||||
|
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
use crate::syntax::ast::Expression;
|
|
||||||
|
|
||||||
use super::Typed;
|
|
||||||
|
|
||||||
|
|
||||||
impl Typed for Expression<'_> {
|
|
||||||
fn get_type(&self) -> String {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
// This crate provides an interface and implementations
|
|
||||||
// for determining the datatypes of the language constructs.
|
|
||||||
|
|
||||||
use crate::lexic::token::Token;
|
|
||||||
|
|
||||||
mod expression;
|
|
||||||
|
|
||||||
|
|
||||||
pub trait Typed {
|
|
||||||
fn get_type(&self) -> String;
|
|
||||||
}
|
|
@ -60,23 +60,26 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParsingResult<Bindin
|
|||||||
return Err(ParsingError::Err(SyntaxError {
|
return Err(ParsingError::Err(SyntaxError {
|
||||||
error_start: token.position,
|
error_start: token.position,
|
||||||
error_end: token.get_end_position(),
|
error_end: token.get_end_position(),
|
||||||
reason: "There should be an identifier after a binding".into(),
|
reason: "??".into(),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
Err(ParsingError::Err(error)) => {
|
||||||
|
return Err(ParsingError::Err(error));
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// The parser didn't find an Identifier after VAL/VAR or the Datatype
|
// The parser didn't find an Identifier after VAL/VAR or the Datatype
|
||||||
match (binding_token, datatype) {
|
match (binding_token, datatype) {
|
||||||
(Some(binding_token), None) => {
|
(Some(binding_token), _) => {
|
||||||
return Err(ParsingError::Err(SyntaxError {
|
return Err(ParsingError::Err(SyntaxError {
|
||||||
reason: format!(
|
reason: format!(
|
||||||
"There should be an identifier after a `{}` token",
|
"There should be an identifier after a `{}` token",
|
||||||
if is_var { "var" } else { "val" }
|
if is_var { "val" } else { "var" }
|
||||||
),
|
),
|
||||||
error_start: binding_token.position,
|
error_start: binding_token.position,
|
||||||
error_end: binding_token.get_end_position(),
|
error_end: binding_token.get_end_position(),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
(_, Some(datatype_token)) => {
|
(None, Some(datatype_token)) => {
|
||||||
return Err(ParsingError::Err(SyntaxError {
|
return Err(ParsingError::Err(SyntaxError {
|
||||||
reason: "There should be an identifier after the datatype".into(),
|
reason: "There should be an identifier after the datatype".into(),
|
||||||
error_start: datatype_token.position,
|
error_start: datatype_token.position,
|
||||||
@ -84,7 +87,7 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParsingResult<Bindin
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
unreachable!("Illegal parser state: binding_token and datatype are both None")
|
panic!("Illegal parser state: binding_token and datatype are both None")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -264,7 +267,6 @@ mod tests {
|
|||||||
Err(ParsingError::Err(error)) => {
|
Err(ParsingError::Err(error)) => {
|
||||||
assert_eq!(4, error.error_start);
|
assert_eq!(4, error.error_start);
|
||||||
assert_eq!(11, error.error_end);
|
assert_eq!(11, error.error_end);
|
||||||
assert_eq!("There should be an identifier after a binding", error.reason);
|
|
||||||
}
|
}
|
||||||
_ => panic!("Error expected"),
|
_ => panic!("Error expected"),
|
||||||
}
|
}
|
||||||
@ -283,65 +285,4 @@ mod tests {
|
|||||||
_ => panic!("Error expected"),
|
_ => panic!("Error expected"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_return_error_when_identifier_is_empty() {
|
|
||||||
let tokens = get_tokens(&String::from("val String ")).unwrap();
|
|
||||||
let binding = try_parse(&tokens, 0);
|
|
||||||
|
|
||||||
match binding {
|
|
||||||
Err(ParsingError::Err(error)) => {
|
|
||||||
assert_eq!(4, error.error_start);
|
|
||||||
assert_eq!(10, error.error_end);
|
|
||||||
assert_eq!("There should be an identifier after the datatype", error.reason);
|
|
||||||
}
|
|
||||||
_ => panic!("Error expected"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_return_error_when_identifier_is_empty_2() {
|
|
||||||
let tokens = get_tokens(&String::from("val ")).unwrap();
|
|
||||||
let binding = try_parse(&tokens, 0);
|
|
||||||
|
|
||||||
match binding {
|
|
||||||
Err(ParsingError::Err(error)) => {
|
|
||||||
assert_eq!(0, error.error_start);
|
|
||||||
assert_eq!(3, error.error_end);
|
|
||||||
assert_eq!("There should be an identifier after a `val` token", error.reason);
|
|
||||||
}
|
|
||||||
_ => panic!("Error expected"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_error_when_equal_op_is_missing() {
|
|
||||||
let tokens = get_tokens(&String::from("val identifier ")).unwrap();
|
|
||||||
let binding = try_parse(&tokens, 0);
|
|
||||||
|
|
||||||
match binding {
|
|
||||||
Err(ParsingError::Err(error)) => {
|
|
||||||
assert_eq!(4, error.error_start);
|
|
||||||
assert_eq!(14, error.error_end);
|
|
||||||
assert_eq!("There should be an equal sign `=` after the identifier", error.reason);
|
|
||||||
}
|
|
||||||
_ => panic!("Error expected"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_error_when_exp_is_empty() {
|
|
||||||
let tokens = get_tokens(&String::from("val identifier = ")).unwrap();
|
|
||||||
let binding = try_parse(&tokens, 0);
|
|
||||||
|
|
||||||
match binding {
|
|
||||||
Err(ParsingError::Err(error)) => {
|
|
||||||
assert_eq!(15, error.error_start);
|
|
||||||
assert_eq!(16, error.error_end);
|
|
||||||
assert_eq!("Expected an expression after the equal `=` operator", error.reason);
|
|
||||||
}
|
|
||||||
_ => panic!("Error expected"),
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -47,45 +47,3 @@ fn parse_many<'a>(
|
|||||||
_ => Ok((prev_expr, pos)),
|
_ => Ok((prev_expr, pos)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::lexic::get_tokens;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_parse_comparison() {
|
|
||||||
let tokens = get_tokens(&String::from("a >= b")).unwrap();
|
|
||||||
let result = try_parse(&tokens, 0);
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Ok((expr, _)) => match expr {
|
|
||||||
Expression::BinaryOperator(exp1, exp2, op) => {
|
|
||||||
match (*exp1, *exp2) {
|
|
||||||
(Expression::Identifier(id1), Expression::Identifier(id2)) => {
|
|
||||||
assert_eq!("a", id1);
|
|
||||||
assert_eq!("b", id2);
|
|
||||||
}
|
|
||||||
_ => panic!("Expected 2 identifiers"),
|
|
||||||
}
|
|
||||||
assert_eq!(">=", op)
|
|
||||||
}
|
|
||||||
_ => panic!("Expected a binary expression with 2 identifiers"),
|
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
panic!("{:?}", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_not_parse_unfinished_comparison() {
|
|
||||||
let tokens = get_tokens(&String::from("a >=")).unwrap();
|
|
||||||
let result = try_parse(&tokens, 0);
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Err(ParsingError::Unmatched) => assert!(true),
|
|
||||||
_ => panic!("Expected an Unmatched error"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -42,45 +42,3 @@ fn parse_many<'a>(
|
|||||||
_ => Ok((prev_expr, pos)),
|
_ => Ok((prev_expr, pos)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::lexic::get_tokens;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_parse_comparison() {
|
|
||||||
let tokens = get_tokens(&String::from("a == b")).unwrap();
|
|
||||||
let result = try_parse(&tokens, 0);
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Ok((expr, _)) => match expr {
|
|
||||||
Expression::BinaryOperator(exp1, exp2, op) => {
|
|
||||||
match (*exp1, *exp2) {
|
|
||||||
(Expression::Identifier(id1), Expression::Identifier(id2)) => {
|
|
||||||
assert_eq!("a", id1);
|
|
||||||
assert_eq!("b", id2);
|
|
||||||
}
|
|
||||||
_ => panic!("Expected 2 identifiers"),
|
|
||||||
}
|
|
||||||
assert_eq!("==", op)
|
|
||||||
}
|
|
||||||
_ => panic!("Expected a binary expression with 2 identifiers"),
|
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
panic!("{:?}", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_not_parse_unfinished_comparison() {
|
|
||||||
let tokens = get_tokens(&String::from("a ==")).unwrap();
|
|
||||||
let result = try_parse(&tokens, 0);
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Err(ParsingError::Unmatched) => assert!(true),
|
|
||||||
_ => panic!("Expected an Unmatched error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -42,47 +42,3 @@ fn parse_many<'a>(
|
|||||||
_ => Ok((prev_expr, pos)),
|
_ => Ok((prev_expr, pos)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::lexic::get_tokens;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_parse_comparison() {
|
|
||||||
let tokens = get_tokens(&String::from("a * b")).unwrap();
|
|
||||||
let result = try_parse(&tokens, 0);
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Ok((expr, _)) => match expr {
|
|
||||||
Expression::BinaryOperator(exp1, exp2, op) => {
|
|
||||||
match (*exp1, *exp2) {
|
|
||||||
(Expression::Identifier(id1), Expression::Identifier(id2)) => {
|
|
||||||
assert_eq!("a", id1);
|
|
||||||
assert_eq!("b", id2);
|
|
||||||
}
|
|
||||||
_ => panic!("Expected 2 identifiers"),
|
|
||||||
}
|
|
||||||
assert_eq!("*", op)
|
|
||||||
}
|
|
||||||
_ => panic!("Expected a binary expression with 2 identifiers"),
|
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
panic!("{:?}", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_not_parse_unfinished_comparison() {
|
|
||||||
let tokens = get_tokens(&String::from("a /")).unwrap();
|
|
||||||
let result = try_parse(&tokens, 0);
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Err(ParsingError::Unmatched) => assert!(true),
|
|
||||||
_ => panic!("Expected an Unmatched error"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -42,45 +42,3 @@ fn parse_many<'a>(
|
|||||||
_ => Ok((prev_expr, pos)),
|
_ => Ok((prev_expr, pos)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::lexic::get_tokens;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_parse_comparison() {
|
|
||||||
let tokens = get_tokens(&String::from("a + b")).unwrap();
|
|
||||||
let result = try_parse(&tokens, 0);
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Ok((expr, _)) => match expr {
|
|
||||||
Expression::BinaryOperator(exp1, exp2, op) => {
|
|
||||||
match (*exp1, *exp2) {
|
|
||||||
(Expression::Identifier(id1), Expression::Identifier(id2)) => {
|
|
||||||
assert_eq!("a", id1);
|
|
||||||
assert_eq!("b", id2);
|
|
||||||
}
|
|
||||||
_ => panic!("Expected 2 identifiers"),
|
|
||||||
}
|
|
||||||
assert_eq!("+", op)
|
|
||||||
}
|
|
||||||
_ => panic!("Expected a binary expression with 2 identifiers"),
|
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
panic!("{:?}", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_not_parse_unfinished_comparison() {
|
|
||||||
let tokens = get_tokens(&String::from("a -")).unwrap();
|
|
||||||
let result = try_parse(&tokens, 0);
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Err(ParsingError::Unmatched) => assert!(true),
|
|
||||||
_ => panic!("Expected an Unmatched error"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user