Compare commits

..

10 Commits

34 changed files with 507 additions and 483 deletions

View File

@ -8,7 +8,6 @@
codegen section can focus only in codegen, not in codegen section can focus only in codegen, not in
translation of thp->php. translation of thp->php.
- Parse __more__ binary operators - Parse __more__ binary operators
- Parse `Type name = value` bindings
- Parse more complex bindings - Parse more complex bindings
- Watch mode - Watch mode
- Improve error messages - Improve error messages
@ -23,18 +22,40 @@
- Simple language server - Simple language server
- Decide how to handle comments in the syntax (?)(should comments mean something like in rust?) - Decide how to handle comments in the syntax (?)(should comments mean something like in rust?)
## v0.0.12
- [ ] Infer datatype of a function call expression
- [ ] Infer datatype of binary operators
- [ ] Execute semantic analysis on the function's block
- [ ] Write tests
## v0.0.11
- [ ] Parse binding of form `val Type variable = value`
- [ ] Parse binding of form `Type variable = value`
- [ ] Infer datatype of `value` in the above for a simple expression
- [ ] Ensure that the anotated datatype matches the datatype of `value` in the above
- [ ] Infer datatype of a `val variable = value` in the AST: Use the infered datatype
- [ ] Formally define the top level constructs
- [ ] Parse bindings and function declarations as top level constructs
- [ ] Parse function declaration arguments (`Type id`)
- [ ] Parse function return datatype (`fun f() -> Type`)
- [ ] Return parsing to variables to var/val
- [ ] Write tests
## v0.0.10 ## v0.0.10
- [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] Begin work on semantic analysis - [x] Begin work on semantic analysis
- [x] Minimal symbol table - [x] Minimal symbol table
- [x] Check duplicate function declarations - [x] Check duplicate function declarations
- [x] Improve REPL/File compilation code - [x] Improve REPL/File compilation code
- [ ] Typecheck bindings - [x] Check binding duplication in it's scope
- [x] Typecheck functions - [x] Check function duplication in it's scope
- [ ] Transform simple THP expression into PHP statements - [x] Transform simple THP expression into PHP statements
## v0.0.9 ## v0.0.9

View File

@ -1,28 +1,36 @@
use super::Transpilable; use super::Transpilable;
use crate::syntax::ast::var_binding::Binding; use crate::syntax::ast::var_binding::Binding;
impl Transpilable for Binding { impl Transpilable for Binding<'_> {
/// Transpiles val and var bindings into PHP. /// Transpiles val and var bindings into PHP.
fn transpile(&self) -> String { fn transpile(&self) -> String {
let expression_str = self.expression.transpile(); let expression_str = self.expression.transpile();
format!("${} = {}", self.identifier, expression_str) format!("${} = {}", self.identifier.value, expression_str)
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::syntax::ast::{var_binding::Binding, Expression}; use crate::{
lexic::token::{Token, TokenType},
syntax::ast::{var_binding::Binding, Expression},
};
#[test] #[test]
fn binding_should_transpile() { fn binding_should_transpile() {
let id = String::from("identifier"); let id = String::from("identifier");
let id_token = Token {
token_type: TokenType::Identifier,
value: id,
position: 0,
};
let value = String::from("322"); let value = String::from("322");
let binding = Binding { let binding = Binding {
datatype: None, datatype: None,
identifier: Box::new(id), identifier: &id_token,
expression: Expression::Number(Box::new(value)), expression: Expression::Number(&value),
is_mutable: false, is_mutable: false,
}; };

View File

@ -2,7 +2,7 @@ use crate::syntax::ast::Block;
use super::Transpilable; use super::Transpilable;
impl Transpilable for Block { impl Transpilable for Block<'_> {
fn transpile(&self) -> String { fn transpile(&self) -> String {
// TODO: Handle indentation // TODO: Handle indentation
self.statements self.statements

View File

@ -1,7 +1,7 @@
use super::Transpilable; use super::Transpilable;
use crate::syntax::ast::Expression; use crate::syntax::ast::Expression;
impl Transpilable for Expression { impl Transpilable for Expression<'_> {
/// Transpiles an Expression to PHP /// Transpiles an Expression to PHP
/// ///
/// Right now the expressions in the grammar are: /// Right now the expressions in the grammar are:
@ -41,7 +41,7 @@ mod tests {
#[test] #[test]
fn should_transpile_number() { fn should_transpile_number() {
let str = String::from("42"); let str = String::from("42");
let exp = Expression::Number(Box::new(str)); let exp = Expression::Number(&str);
let result = exp.transpile(); let result = exp.transpile();
assert_eq!("42", result); assert_eq!("42", result);
@ -50,7 +50,7 @@ mod tests {
#[test] #[test]
fn should_transpile_string() { fn should_transpile_string() {
let str = String::from("\"Hello world\""); let str = String::from("\"Hello world\"");
let exp = Expression::String(Box::new(str)); let exp = Expression::String(&str);
let result = exp.transpile(); let result = exp.transpile();
assert_eq!("\"Hello world\"", result); assert_eq!("\"Hello world\"", result);
@ -67,7 +67,7 @@ mod tests {
#[test] #[test]
fn should_transpile_identifier() { fn should_transpile_identifier() {
let s = String::from("newValue"); let s = String::from("newValue");
let exp = Expression::Identifier(Box::new(s)); let exp = Expression::Identifier(&s);
let result = exp.transpile(); let result = exp.transpile();
assert_eq!("newValue", result); assert_eq!("newValue", result);

View File

@ -2,7 +2,7 @@ use crate::syntax::ast::functions::FunctionCall;
use super::Transpilable; use super::Transpilable;
impl Transpilable for FunctionCall { impl Transpilable for FunctionCall<'_> {
fn transpile(&self) -> String { fn transpile(&self) -> String {
let parameters = &self let parameters = &self
.arguments .arguments

View File

@ -2,11 +2,11 @@ use crate::syntax::ast::FunctionDeclaration;
use super::Transpilable; use super::Transpilable;
impl Transpilable for FunctionDeclaration { impl Transpilable for FunctionDeclaration<'_> {
fn transpile(&self) -> String { fn transpile(&self) -> String {
format!( format!(
"function {}() {{\n{}\n}}", "function {}() {{\n{}\n}}",
self.identifier, self.identifier.value,
self.block.transpile() self.block.transpile()
) )
} }
@ -34,6 +34,7 @@ mod tests {
assert_eq!("function id() {\n\n}", transpiled); assert_eq!("function id() {\n\n}", transpiled);
} }
_ => panic!("Not implemented: Expression at top level"),
} }
} }
} }

View File

@ -1,7 +1,7 @@
use super::Transpilable; use super::Transpilable;
use crate::syntax::ast::ModuleAST; use crate::syntax::ast::ModuleAST;
impl Transpilable for ModuleAST { impl Transpilable for ModuleAST<'_> {
/// Transpiles the whole AST into PHP, using this same trait on the /// Transpiles the whole AST into PHP, using this same trait on the
/// nodes and leaves of the AST /// nodes and leaves of the AST
fn transpile(&self) -> String { fn transpile(&self) -> String {
@ -18,16 +18,24 @@ impl Transpilable for ModuleAST {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::syntax::ast::{var_binding::Binding, Expression, TopLevelDeclaration}; use crate::{
lexic::token::{Token, TokenType},
syntax::ast::{var_binding::Binding, Expression, TopLevelDeclaration},
};
#[test] #[test]
fn module_ast_should_transpile() { fn module_ast_should_transpile() {
let id = String::from("identifier"); let id = String::from("identifier");
let id_token = Token {
token_type: TokenType::Identifier,
value: id,
position: 0,
};
let value = String::from("322"); let value = String::from("322");
let binding = Binding { let binding = Binding {
datatype: None, datatype: None,
identifier: Box::new(id), identifier: &id_token,
expression: Expression::Number(Box::new(value)), expression: Expression::Number(&value),
is_mutable: false, is_mutable: false,
}; };

View File

@ -2,7 +2,7 @@ use crate::syntax::ast::statement::Statement;
use super::Transpilable; use super::Transpilable;
impl Transpilable for Statement { impl Transpilable for Statement<'_> {
fn transpile(&self) -> String { fn transpile(&self) -> String {
let stmt = match self { let stmt = match self {
Statement::FunctionCall(f) => f.transpile(), Statement::FunctionCall(f) => f.transpile(),

View File

@ -2,11 +2,12 @@ use crate::syntax::ast::TopLevelDeclaration;
use super::Transpilable; use super::Transpilable;
impl Transpilable for TopLevelDeclaration { impl Transpilable for TopLevelDeclaration<'_> {
fn transpile(&self) -> String { fn transpile(&self) -> String {
match self { match self {
TopLevelDeclaration::Binding(binding) => binding.transpile(), TopLevelDeclaration::Binding(binding) => binding.transpile(),
TopLevelDeclaration::FunctionDeclaration(fun) => fun.transpile(), TopLevelDeclaration::FunctionDeclaration(fun) => fun.transpile(),
_ => panic!("Not implemented: Expression at top level"),
} }
} }
} }

View File

@ -22,7 +22,7 @@ pub enum TokenType {
FUN, FUN,
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Token { pub struct Token {
pub token_type: TokenType, pub token_type: TokenType,
// The token as a raw string // The token as a raw string

View File

@ -13,7 +13,6 @@ mod lexic;
mod semantic; mod semantic;
// Transforms an AST to JS // Transforms an AST to JS
mod codegen; mod codegen;
mod utils;
mod error_handling; mod error_handling;

View File

@ -4,13 +4,13 @@ use crate::{
syntax::ast::{ModuleAST, TopLevelDeclaration}, syntax::ast::{ModuleAST, TopLevelDeclaration},
}; };
use super::symbol_table::SymbolTable; use super::symbol_table::{SymbolEntry, SymbolTable};
pub trait SemanticCheck { pub trait SemanticCheck {
fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError>; fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError>;
} }
impl SemanticCheck for ModuleAST { impl SemanticCheck for ModuleAST<'_> {
/// Checks that this AST is semantically correct, given a symbol table /// Checks that this AST is semantically correct, given a symbol table
fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError> { fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError> {
for declaration in &self.declarations { for declaration in &self.declarations {
@ -21,28 +21,60 @@ impl SemanticCheck for ModuleAST {
} }
} }
impl SemanticCheck for TopLevelDeclaration { impl SemanticCheck for TopLevelDeclaration<'_> {
fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError> { fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError> {
match self { match self {
TopLevelDeclaration::Binding(_) => { TopLevelDeclaration::Binding(binding) => {
let binding_name = &binding.identifier.value;
if scope.test(binding_name) {
let error = SemanticError { let error = SemanticError {
error_start: 0, error_start: binding.identifier.position,
error_end: 0, error_end: binding.identifier.get_end_position(),
reason: "Binding typechecking: Not implemented".into(), reason: format!(
"Duplicated function: A function with name {} was already defined",
binding_name
),
}; };
Err(MistiError::Semantic(error)) 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) => { TopLevelDeclaration::FunctionDeclaration(function) => {
let function_name = function.identifier.as_ref().clone(); let function_name = function.identifier.value.clone();
// Check that the function is not already defined
if scope.test(&function_name) { if scope.test(&function_name) {
let error = SemanticError { let error = SemanticError {
// TODO: Get the position of the function name. For this, these structs error_start: function.identifier.position,
// should store the token instead of just the string error_end: function.identifier.get_end_position(),
error_start: 0, reason: format!(
error_end: 0, "Duplicated function: A function with name {} was already defined",
reason: format!("Function {} already defined", function_name), function_name
),
}; };
return Err(MistiError::Semantic(error)); return Err(MistiError::Semantic(error));
@ -50,11 +82,12 @@ impl SemanticCheck for TopLevelDeclaration {
scope.insert( scope.insert(
function_name, function_name,
super::symbol_table::SymbolEntry::Function(vec![], "Unit".into()), SymbolEntry::new_function(vec![], "Unit".into()),
); );
Ok(()) Ok(())
} }
_ => panic!("Not implemented"),
} }
} }
} }

View File

@ -1,12 +1,12 @@
use super::Expression; use super::Expression;
#[derive(Debug)] #[derive(Debug)]
pub struct FunctionCall { pub struct FunctionCall<'a> {
pub function: Box<Expression>, pub function: Box<Expression<'a>>,
pub arguments: Box<ArgumentsList>, pub arguments: Box<ArgumentsList<'a>>,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct ArgumentsList { pub struct ArgumentsList<'a> {
pub arguments: Vec<Expression>, pub arguments: Vec<Expression<'a>>,
} }

View File

@ -1,46 +1,49 @@
use crate::lexic::token::Token;
use self::functions::FunctionCall; use self::functions::FunctionCall;
pub mod functions; pub mod functions;
pub mod statement; pub mod statement;
pub mod var_binding; pub mod var_binding;
pub struct ModuleAST { pub struct ModuleAST<'a> {
pub declarations: Vec<TopLevelDeclaration>, pub declarations: Vec<TopLevelDeclaration<'a>>,
} }
#[derive(Debug)] #[derive(Debug)]
pub enum TopLevelDeclaration { pub enum TopLevelDeclaration<'a> {
Binding(var_binding::Binding), Binding(var_binding::Binding<'a>),
FunctionDeclaration(FunctionDeclaration), FunctionDeclaration(FunctionDeclaration<'a>),
Expression(Expression<'a>),
} }
#[derive(Debug)] #[derive(Debug)]
pub struct FunctionDeclaration { pub struct FunctionDeclaration<'a> {
pub identifier: Box<String>, pub identifier: &'a Token,
pub params_list: Box<ParamsList>, pub params_list: Box<ParamsList>,
pub block: Box<Block>, pub block: Box<Block<'a>>,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Block { pub struct Block<'a> {
pub statements: Vec<statement::Statement>, pub statements: Vec<statement::Statement<'a>>,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct ParamsList {} pub struct ParamsList {}
pub struct Parameter { pub struct Parameter<'a> {
pub identifier: Box<String>, pub identifier: &'a String,
pub datatype: Box<String>, pub datatype: &'a String,
} }
#[derive(Debug)] #[derive(Debug)]
pub enum Expression { pub enum Expression<'a> {
Number(Box<String>), Number(&'a String),
String(Box<String>), String(&'a String),
Boolean(bool), Boolean(bool),
Identifier(Box<String>), Identifier(&'a String),
FunctionCall(FunctionCall), FunctionCall(FunctionCall<'a>),
UnaryOperator(Box<String>, Box<Expression>), UnaryOperator(&'a String, Box<Expression<'a>>),
BinaryOperator(Box<Expression>, Box<Expression>, Box<String>), BinaryOperator(Box<Expression<'a>>, Box<Expression<'a>>, &'a String),
} }

View File

@ -1,7 +1,7 @@
use super::{functions::FunctionCall, var_binding::Binding}; use super::{functions::FunctionCall, var_binding::Binding};
#[derive(Debug)] #[derive(Debug)]
pub enum Statement { pub enum Statement<'a> {
FunctionCall(FunctionCall), FunctionCall(FunctionCall<'a>),
Binding(Binding), Binding(Binding<'a>),
} }

View File

@ -1,9 +1,11 @@
use crate::lexic::token::Token;
use super::Expression; use super::Expression;
#[derive(Debug)] #[derive(Debug)]
pub struct Binding { pub struct Binding<'a> {
pub datatype: Option<String>, pub datatype: Option<&'a Token>,
pub identifier: Box<String>, pub identifier: &'a Token,
pub expression: Expression, pub expression: Expression<'a>,
pub is_mutable: bool, pub is_mutable: bool,
} }

View File

@ -1,14 +1,14 @@
use super::ast::var_binding::Binding; use super::ast::var_binding::Binding;
use super::utils::{parse_token_type, try_operator}; use super::utils::{parse_token_type, try_operator};
use super::{expression, ParseResult}; use super::{expression, ParsingError, ParsingResult};
use crate::error_handling::SyntaxError; use crate::error_handling::SyntaxError;
use crate::lexic::token::{Token, TokenType}; use crate::lexic::token::{Token, TokenType};
use crate::utils::Result3;
pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Binding, ()> { pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParsingResult<Binding> {
let mut current_pos = pos; let mut current_pos = pos;
// TODO: Detect if the binding starts with a datatype // TODO: Detect if the binding starts with a datatype
// TODO: Revert to val/var
/* /*
* let keyword * let keyword
@ -16,14 +16,14 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Binding,
let (is_mutable, binding_token, next_pos) = { let (is_mutable, binding_token, next_pos) = {
let let_token = parse_token_type(tokens, current_pos, TokenType::LET); let let_token = parse_token_type(tokens, current_pos, TokenType::LET);
match let_token { match let_token {
ParseResult::Ok(let_token, next_let) => { Ok((let_token, next_let)) => {
let mut_token = parse_token_type(tokens, next_let, TokenType::MUT); let mut_token = parse_token_type(tokens, next_let, TokenType::MUT);
match mut_token { match mut_token {
ParseResult::Ok(_mut_token, next_mut) => (true, let_token, next_mut), Ok((_mut_token, next_mut)) => (true, let_token, next_mut),
_ => (false, let_token, next_let), _ => (false, let_token, next_let),
} }
} }
_ => return ParseResult::Unmatched, _ => return Err(ParsingError::Unmatched),
} }
}; };
current_pos = next_pos; current_pos = next_pos;
@ -33,28 +33,28 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Binding,
*/ */
let (identifier, next_pos) = match parse_token_type(tokens, current_pos, TokenType::Identifier) let (identifier, next_pos) = match parse_token_type(tokens, current_pos, TokenType::Identifier)
{ {
ParseResult::Ok(t, n) => (t, n), Ok((t, n)) => (t, n),
ParseResult::Mismatch(token) => { Err(ParsingError::Mismatch(token)) => {
// The parser found a token, but it's not an identifier // The parser found a token, but it's not an identifier
return ParseResult::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: "??".into(), reason: "??".into(),
}); }));
} }
ParseResult::Err(error) => { Err(ParsingError::Err(error)) => {
return ParseResult::Err(error); return Err(ParsingError::Err(error));
} }
_ => { _ => {
// The parser didn't find an Identifier after VAL/VAR // The parser didn't find an Identifier after VAL/VAR
return ParseResult::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_mutable { "val" } else { "var" } if is_mutable { "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(),
}); }));
} }
}; };
current_pos = next_pos; current_pos = next_pos;
@ -63,67 +63,67 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Binding,
* Equal (=) operator * Equal (=) operator
*/ */
let equal_operator = match try_operator(tokens, current_pos, String::from("=")) { let equal_operator = match try_operator(tokens, current_pos, String::from("=")) {
Result3::Ok(t) => t, Ok((t, _)) => t,
Result3::Err(t) => { Err(ParsingError::Mismatch(t)) => {
// The parser found a token, but it's not the `=` operator // The parser found a token, but it's not the `=` operator
return ParseResult::Err(SyntaxError { return Err(ParsingError::Err(SyntaxError {
reason: format!("There should be an equal sign `=` after the identifier"), reason: format!("There should be an equal sign `=` after the identifier"),
error_start: t.position, error_start: t.position,
error_end: t.get_end_position(), error_end: t.get_end_position(),
}); }));
} }
Result3::None => { _ => {
// The parser didn't find the `=` operator after the identifier // The parser didn't find the `=` operator after the identifier
return ParseResult::Err(SyntaxError { return Err(ParsingError::Err(SyntaxError {
reason: format!("There should be an equal sign `=` after the identifier",), reason: format!("There should be an equal sign `=` after the identifier",),
error_start: identifier.position, error_start: identifier.position,
error_end: identifier.get_end_position(), error_end: identifier.get_end_position(),
}); }));
} }
}; };
current_pos += 1; current_pos += 1;
let (expression, next_pos) = match expression::try_parse(tokens, current_pos) { let (expression, next_pos) = match expression::try_parse(tokens, current_pos) {
ParseResult::Ok(exp, next) => (exp, next), Ok((exp, next)) => (exp, next),
_ => { _ => {
return ParseResult::Err(SyntaxError { return Err(ParsingError::Err(SyntaxError {
reason: String::from("Expected an expression after the equal `=` operator"), reason: String::from("Expected an expression after the equal `=` operator"),
error_start: equal_operator.position, error_start: equal_operator.position,
error_end: equal_operator.get_end_position(), error_end: equal_operator.get_end_position(),
}); }));
} }
}; };
current_pos = next_pos; current_pos = next_pos;
let binding = Binding { let binding = Binding {
datatype: None, datatype: None,
identifier: Box::new(identifier.value.clone()), identifier: &identifier,
expression, expression,
is_mutable, is_mutable,
}; };
ParseResult::Ok(binding, current_pos) Ok((binding, current_pos))
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{lexic::get_tokens, syntax::utils::try_token_type}; use crate::{lexic::get_tokens, syntax::utils::parse_token_type};
#[test] #[test]
fn should_parse_val_binding() { fn should_parse_val_binding() {
let tokens = get_tokens(&String::from("let identifier = 20")).unwrap(); let tokens = get_tokens(&String::from("let identifier = 20")).unwrap();
let ParseResult::Ok(binding, _) = try_parse(&tokens, 0) else { let Ok((binding, _)) = try_parse(&tokens, 0) else {
panic!() panic!()
}; };
assert_eq!("identifier", format!("{}", binding.identifier)); assert_eq!("identifier", format!("{}", binding.identifier.value));
} }
#[test] #[test]
fn should_parse_val() { fn should_parse_val() {
let tokens = get_tokens(&String::from("let")).unwrap(); let tokens = get_tokens(&String::from("let")).unwrap();
let token = *try_token_type(&tokens, 0, TokenType::LET).unwrap(); let (token, _) = parse_token_type(&tokens, 0, TokenType::LET).unwrap();
assert_eq!(TokenType::LET, token.token_type); assert_eq!(TokenType::LET, token.token_type);
assert_eq!("let", token.value); assert_eq!("let", token.value);
@ -132,7 +132,7 @@ mod tests {
#[test] #[test]
fn should_parse_identifier() { fn should_parse_identifier() {
let tokens = get_tokens(&String::from("identifier")).unwrap(); let tokens = get_tokens(&String::from("identifier")).unwrap();
let token = *try_token_type(&tokens, 0, TokenType::Identifier).unwrap(); let (token, _) = parse_token_type(&tokens, 0, TokenType::Identifier).unwrap();
assert_eq!("identifier", token.value); assert_eq!("identifier", token.value);
} }
@ -140,7 +140,7 @@ mod tests {
#[test] #[test]
fn should_parse_operator() { fn should_parse_operator() {
let tokens = get_tokens(&String::from("=")).unwrap(); let tokens = get_tokens(&String::from("=")).unwrap();
let token = *try_operator(&tokens, 0, String::from("=")).unwrap(); let (token, _) = try_operator(&tokens, 0, String::from("=")).unwrap();
assert_eq!("=", token.value); assert_eq!("=", token.value);
} }
@ -174,7 +174,7 @@ mod tests {
let binding = try_parse(&tokens, 0); let binding = try_parse(&tokens, 0);
match binding { match binding {
ParseResult::Err(error) => { Err(ParsingError::Err(error)) => {
assert_eq!(0, error.error_start); assert_eq!(0, error.error_start);
assert_eq!(3, error.error_end); assert_eq!(3, error.error_end);
} }
@ -190,7 +190,7 @@ mod tests {
let binding = try_parse(&tokens, 0); let binding = try_parse(&tokens, 0);
match binding { match binding {
ParseResult::Err(error) => { Err(ParsingError::Err(error)) => {
assert_eq!(4, error.error_start); assert_eq!(4, error.error_start);
assert_eq!(7, error.error_end); assert_eq!(7, error.error_end);
} }
@ -201,7 +201,7 @@ mod tests {
let binding = try_parse(&tokens, 0); let binding = try_parse(&tokens, 0);
match binding { match binding {
ParseResult::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);
} }
@ -215,7 +215,7 @@ mod tests {
let binding = try_parse(&tokens, 0); let binding = try_parse(&tokens, 0);
match binding { match binding {
ParseResult::Err(error) => { Err(ParsingError::Err(error)) => {
assert_eq!(7, error.error_start); assert_eq!(7, error.error_start);
assert_eq!(14, error.error_end); assert_eq!(14, error.error_end);
} }

View File

@ -3,19 +3,13 @@ use crate::{
lexic::token::{Token, TokenType}, lexic::token::{Token, TokenType},
}; };
use super::{ast::Block, utils::parse_token_type, ParseResult}; use super::{ast::Block, utils::parse_token_type, ParsingError, ParsingResult};
// Assumes that the token at `pos` is a { // Assumes that the token at `pos` is a {
pub fn parse_block<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Block, &Token> { pub fn parse_block<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParsingResult<Block> {
let mut current_pos = pos; let mut current_pos = pos;
let (opening_brace, next_pos) = let (opening_brace, next_pos) = parse_token_type(tokens, current_pos, TokenType::LeftBrace)?;
match parse_token_type(tokens, current_pos, TokenType::LeftBrace) {
ParseResult::Ok(t, next) => (t, next),
ParseResult::Err(err) => return ParseResult::Err(err),
ParseResult::Mismatch(t) => return ParseResult::Mismatch(t),
ParseResult::Unmatched => return ParseResult::Unmatched,
};
current_pos = next_pos; current_pos = next_pos;
// Parse block statements // Parse block statements
@ -23,13 +17,12 @@ pub fn parse_block<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Block,
// First statement // First statement
match super::statement::try_parse(tokens, current_pos) { match super::statement::try_parse(tokens, current_pos) {
ParseResult::Ok(statement, next_pos) => { Ok((statement, next_pos)) => {
current_pos = next_pos; current_pos = next_pos;
statements.push(statement); statements.push(statement);
} }
ParseResult::Err(err) => return ParseResult::Err(err), Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
ParseResult::Unmatched => {} _ => {}
ParseResult::Mismatch(_) => {}
} }
// More statements separated by new lines // More statements separated by new lines
@ -40,11 +33,11 @@ pub fn parse_block<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Block,
current_pos += 1; current_pos += 1;
match super::statement::try_parse(tokens, current_pos) { match super::statement::try_parse(tokens, current_pos) {
ParseResult::Ok(statement, next_pos) => { Ok((statement, next_pos)) => {
current_pos = next_pos; current_pos = next_pos;
statements.push(statement); statements.push(statement);
} }
ParseResult::Err(err) => return ParseResult::Err(err), Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
_ => break, _ => break,
} }
} }
@ -52,26 +45,26 @@ pub fn parse_block<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Block,
// Parse closing brace // Parse closing brace
let (_closing_brace, next_pos) = let (_closing_brace, next_pos) =
match parse_token_type(tokens, current_pos, TokenType::RightBrace) { match parse_token_type(tokens, current_pos, TokenType::RightBrace) {
ParseResult::Ok(t, next) => (t, next), Ok((t, next)) => (t, next),
ParseResult::Err(err) => return ParseResult::Err(err), Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
ParseResult::Mismatch(t) => { Err(ParsingError::Mismatch(t)) => {
return ParseResult::Err(SyntaxError { return Err(ParsingError::Err(SyntaxError {
reason: String::from("Expected a closing brace after the block body."), reason: String::from("Expected a closing brace after the block body."),
error_start: t.position, error_start: t.position,
error_end: t.get_end_position(), error_end: t.get_end_position(),
}); }));
} }
ParseResult::Unmatched => { Err(ParsingError::Unmatched) => {
return ParseResult::Err(SyntaxError { return Err(ParsingError::Err(SyntaxError {
reason: String::from("Expected a closing brace after the block body."), reason: String::from("Expected a closing brace after the block body."),
error_start: opening_brace.position, error_start: opening_brace.position,
error_end: opening_brace.get_end_position(), error_end: opening_brace.get_end_position(),
}); }));
} }
}; };
current_pos = next_pos; current_pos = next_pos;
ParseResult::Ok(Block { statements }, current_pos) Ok((Block { statements }, current_pos))
} }
#[cfg(test)] #[cfg(test)]
@ -85,7 +78,7 @@ mod tests {
let block = parse_block(&tokens, 0); let block = parse_block(&tokens, 0);
let block = match block { let block = match block {
ParseResult::Ok(p, _) => p, ParsingResult::Ok((p, _)) => p,
_ => panic!("Expected a block, got: {:?}", block), _ => panic!("Expected a block, got: {:?}", block),
}; };
@ -98,7 +91,7 @@ mod tests {
let block = parse_block(&tokens, 0); let block = parse_block(&tokens, 0);
let block = match block { let block = match block {
ParseResult::Ok(p, _) => p, ParsingResult::Ok((p, _)) => p,
_ => panic!("Expected a block, got: {:?}", block), _ => panic!("Expected a block, got: {:?}", block),
}; };
@ -111,7 +104,7 @@ mod tests {
let block = parse_block(&tokens, 0); let block = parse_block(&tokens, 0);
let block = match block { let block = match block {
ParseResult::Ok(p, _) => p, ParsingResult::Ok((p, _)) => p,
_ => { _ => {
panic!("Expected a block, got: {:?}\n\n{:?}", block, tokens) panic!("Expected a block, got: {:?}\n\n{:?}", block, tokens)
} }

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
lexic::token::Token, lexic::token::Token,
syntax::{ast::Expression, ParseResult}, syntax::{ast::Expression, ParsingError, ParsingResult},
}; };
/// Parses a factor expression. /// Parses a factor expression.
@ -8,20 +8,20 @@ use crate::{
/// ```ebnf /// ```ebnf
/// comparison = term, ((">" | ">=" | "<" | "<="), term)*; /// comparison = term, ((">" | ">=" | "<" | "<="), term)*;
/// ``` /// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> { pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
let (term, next_pos) = match super::term::try_parse(tokens, pos) { let (term, next_pos) = match super::term::try_parse(tokens, pos) {
ParseResult::Ok(expr, next_pos) => (expr, next_pos), Ok((expr, next_pos)) => (expr, next_pos),
_ => return ParseResult::Unmatched, _ => return Err(ParsingError::Unmatched),
}; };
parse_many(tokens, next_pos, term) parse_many(tokens, next_pos, term)
} }
fn parse_many( fn parse_many<'a>(
tokens: &Vec<Token>, tokens: &'a Vec<Token>,
pos: usize, pos: usize,
prev_expr: Expression, prev_expr: Expression<'a>,
) -> ParseResult<Expression, ()> { ) -> ParsingResult<'a, Expression<'a>> {
// comparison = term, ((">" | ">=" | "<" | "<="), term)*; // comparison = term, ((">" | ">=" | "<" | "<="), term)*;
match tokens.get(pos) { match tokens.get(pos) {
@ -32,18 +32,18 @@ fn parse_many(
|| token.value == ">=" => || token.value == ">=" =>
{ {
match super::term::try_parse(tokens, pos + 1) { match super::term::try_parse(tokens, pos + 1) {
ParseResult::Ok(expr, next_pos) => { Ok((expr, next_pos)) => {
let expr = Expression::BinaryOperator( let expr = Expression::BinaryOperator(
Box::new(prev_expr), Box::new(prev_expr),
Box::new(expr), Box::new(expr),
Box::new(token.value.clone()), &token.value,
); );
parse_many(tokens, next_pos, expr) parse_many(tokens, next_pos, expr)
} }
_ => ParseResult::Unmatched, _ => Err(ParsingError::Unmatched),
} }
} }
_ => ParseResult::Ok(prev_expr, pos), _ => Ok((prev_expr, pos)),
} }
} }

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
lexic::token::Token, lexic::token::Token,
syntax::{ast::Expression, ParseResult}, syntax::{ast::Expression, ParsingError, ParsingResult},
}; };
/// Parses a factor expression. /// Parses a factor expression.
@ -8,37 +8,37 @@ use crate::{
/// ```ebnf /// ```ebnf
/// equality = comparison, (("==" | "!="), comparison )*; /// equality = comparison, (("==" | "!="), comparison )*;
/// ``` /// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> { pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
let (comparison, next_pos) = match super::comparison::try_parse(tokens, pos) { let (comparison, next_pos) = match super::comparison::try_parse(tokens, pos) {
ParseResult::Ok(expr, next_pos) => (expr, next_pos), Ok((expr, next_pos)) => (expr, next_pos),
_ => return ParseResult::Unmatched, _ => return Err(ParsingError::Unmatched),
}; };
parse_many(tokens, next_pos, comparison) parse_many(tokens, next_pos, comparison)
} }
fn parse_many( fn parse_many<'a>(
tokens: &Vec<Token>, tokens: &'a Vec<Token>,
pos: usize, pos: usize,
prev_expr: Expression, prev_expr: Expression<'a>,
) -> ParseResult<Expression, ()> { ) -> ParsingResult<'a, Expression<'a>> {
// equality = comparison, (("==" | "!="), comparison )*; // equality = comparison, (("==" | "!="), comparison )*;
match tokens.get(pos) { match tokens.get(pos) {
Some(token) if token.value == "==" || token.value == "!=" => { Some(token) if token.value == "==" || token.value == "!=" => {
match super::comparison::try_parse(tokens, pos + 1) { match super::comparison::try_parse(tokens, pos + 1) {
ParseResult::Ok(expr, next_pos) => { Ok((expr, next_pos)) => {
let expr = Expression::BinaryOperator( let expr = Expression::BinaryOperator(
Box::new(prev_expr), Box::new(prev_expr),
Box::new(expr), Box::new(expr),
Box::new(token.value.clone()), &token.value,
); );
parse_many(tokens, next_pos, expr) parse_many(tokens, next_pos, expr)
} }
_ => ParseResult::Unmatched, _ => Err(ParsingError::Unmatched),
} }
} }
_ => ParseResult::Ok(prev_expr, pos), _ => Ok((prev_expr, pos)),
} }
} }

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
lexic::token::Token, lexic::token::Token,
syntax::{ast::Expression, ParseResult}, syntax::{ast::Expression, ParsingError, ParsingResult},
}; };
/// Parses a factor expression. /// Parses a factor expression.
@ -8,37 +8,37 @@ use crate::{
/// ```ebnf /// ```ebnf
/// factor = unary, (("/" | "*"), unary)*; /// factor = unary, (("/" | "*"), unary)*;
/// ``` /// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> { pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
let (unary, next_pos) = match super::unary::try_parse(tokens, pos) { let (unary, next_pos) = match super::unary::try_parse(tokens, pos) {
ParseResult::Ok(expr, next_pos) => (expr, next_pos), Ok((expr, next_pos)) => (expr, next_pos),
_ => return ParseResult::Unmatched, _ => return Err(ParsingError::Unmatched),
}; };
parse_many(tokens, next_pos, unary) parse_many(tokens, next_pos, unary)
} }
fn parse_many( fn parse_many<'a>(
tokens: &Vec<Token>, tokens: &'a Vec<Token>,
pos: usize, pos: usize,
prev_expr: Expression, prev_expr: Expression<'a>,
) -> ParseResult<Expression, ()> { ) -> ParsingResult<'a, Expression<'a>> {
// (("/" | "*"), unary)* // (("/" | "*"), unary)*
match tokens.get(pos) { match tokens.get(pos) {
Some(token) if token.value == "/" || token.value == "*" => { Some(token) if token.value == "/" || token.value == "*" => {
match super::unary::try_parse(tokens, pos + 1) { match super::unary::try_parse(tokens, pos + 1) {
ParseResult::Ok(expr, next_pos) => { Ok((expr, next_pos)) => {
let expr = Expression::BinaryOperator( let expr = Expression::BinaryOperator(
Box::new(prev_expr), Box::new(prev_expr),
Box::new(expr), Box::new(expr),
Box::new(token.value.clone()), &token.value,
); );
parse_many(tokens, next_pos, expr) parse_many(tokens, next_pos, expr)
} }
_ => ParseResult::Unmatched, _ => Err(ParsingError::Unmatched),
} }
} }
_ => ParseResult::Ok(prev_expr, pos), _ => Ok((prev_expr, pos)),
} }
} }

View File

@ -3,7 +3,7 @@ use crate::{
syntax::{ syntax::{
ast::{functions::FunctionCall, Expression}, ast::{functions::FunctionCall, Expression},
functions::arguments_list, functions::arguments_list,
ParseResult, ParsingError, ParsingResult,
}, },
}; };
@ -13,18 +13,18 @@ use crate::{
/// function call expr = primary, "(", (arguments list)?, ")" /// function call expr = primary, "(", (arguments list)?, ")"
/// | primary; /// | primary;
/// ``` /// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> { pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
let (primary_expr, next_pos) = match super::primary::try_parse(tokens, pos) { let (primary_expr, next_pos) = match super::primary::try_parse(tokens, pos) {
ParseResult::Ok(expr, next_pos) => (expr, next_pos), Ok((expr, next_pos)) => (expr, next_pos),
_ => return ParseResult::Unmatched, _ => return Err(ParsingError::Unmatched),
}; };
// Parse arguments list // Parse arguments list
let (arguments, next_pos) = match arguments_list::try_parse(tokens, next_pos) { let (arguments, next_pos) = match arguments_list::try_parse(tokens, next_pos) {
ParseResult::Ok(args, next) => (args, next), Ok((args, next)) => (args, next),
ParseResult::Err(err) => return ParseResult::Err(err), Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
_ => { _ => {
return ParseResult::Ok(primary_expr, next_pos); return Ok((primary_expr, next_pos));
} }
}; };
@ -33,7 +33,7 @@ pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()>
arguments: Box::new(arguments), arguments: Box::new(arguments),
}; };
ParseResult::Ok(Expression::FunctionCall(fun_call), next_pos) Ok((Expression::FunctionCall(fun_call), next_pos))
} }
#[cfg(test)] #[cfg(test)]

View File

@ -1,4 +1,4 @@
use super::{ast::Expression, ParseResult}; use super::{ast::Expression, ParsingResult};
use crate::lexic::token::Token; use crate::lexic::token::Token;
mod comparison; mod comparison;
@ -10,8 +10,8 @@ mod term;
mod unary; mod unary;
/// Expression is defined in the grammar. /// Expression is defined in the grammar.
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> { pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
return equality::try_parse(tokens, pos); equality::try_parse(tokens, pos)
} }
#[cfg(test)] #[cfg(test)]

View File

@ -1,7 +1,7 @@
use super::super::utils::Tokenizer; use super::super::utils::Tokenizer;
use crate::{ use crate::{
lexic::token::{Token, TokenType}, lexic::token::{Token, TokenType},
syntax::{ast::Expression, ParseResult}, syntax::{ast::Expression, ParsingError, ParsingResult},
}; };
/// This grammar may not be up to date. Refer to the spec for the latest grammar. /// This grammar may not be up to date. Refer to the spec for the latest grammar.
@ -9,42 +9,33 @@ use crate::{
/// ```ebnf /// ```ebnf
/// primary = number | string | boolean | identifier | ("(", expression, ")"); /// primary = number | string | boolean | identifier | ("(", expression, ")");
/// ``` /// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> { pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
match tokens.get_significant(pos) { match tokens.get_significant(pos) {
Some((token, token_pos)) => match token.token_type { Some((token, token_pos)) => match token.token_type {
TokenType::Number => ParseResult::Ok( TokenType::Number => Ok((Expression::Number(&token.value), token_pos + 1)),
Expression::Number(Box::new(token.value.clone())), TokenType::String => Ok((Expression::String(&token.value), token_pos + 1)),
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" => { TokenType::Identifier if token.value == "true" || token.value == "false" => {
ParseResult::Ok(Expression::Boolean(token.value == "true"), token_pos + 1) Ok((Expression::Boolean(token.value == "true"), token_pos + 1))
} }
TokenType::Identifier => ParseResult::Ok( TokenType::Identifier => Ok((Expression::Identifier(&token.value), token_pos + 1)),
Expression::Identifier(Box::new(token.value.clone())),
token_pos + 1,
),
TokenType::LeftParen => parse_parenthesized_expression(tokens, token_pos), TokenType::LeftParen => parse_parenthesized_expression(tokens, token_pos),
_ => ParseResult::Unmatched, _ => Err(ParsingError::Unmatched),
}, },
None => ParseResult::Unmatched, None => Err(ParsingError::Unmatched),
} }
} }
fn parse_parenthesized_expression(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> { fn parse_parenthesized_expression(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
let expression = super::try_parse(tokens, pos + 1); let expression = super::try_parse(tokens, pos + 1);
match expression { match expression {
ParseResult::Ok(expression, next_pos) => match tokens.get(next_pos) { Ok((expression, next_pos)) => match tokens.get(next_pos) {
Some(token) => match token.token_type { Some(token) => match token.token_type {
TokenType::RightParen => ParseResult::Ok(expression, next_pos + 1), TokenType::RightParen => Ok((expression, next_pos + 1)),
_ => ParseResult::Unmatched, _ => Err(ParsingError::Unmatched),
}, },
None => ParseResult::Unmatched, None => Err(ParsingError::Unmatched),
}, },
_ => ParseResult::Unmatched, _ => Err(ParsingError::Unmatched),
} }
} }
@ -59,7 +50,9 @@ mod tests {
let expression = try_parse(&tokens, 0); let expression = try_parse(&tokens, 0);
match expression { match expression {
ParseResult::Ok(Expression::Number(value), _) => assert_eq!("40", format!("{}", value)), Ok((Expression::Number(value), _)) => {
assert_eq!("40", format!("{}", value))
}
_ => panic!(), _ => panic!(),
} }
} }
@ -70,7 +63,7 @@ mod tests {
let expression = try_parse(&tokens, 0); let expression = try_parse(&tokens, 0);
match expression { match expression {
ParseResult::Ok(Expression::String(value), _) => { Ok((Expression::String(value), _)) => {
assert_eq!("\"Hello\"", format!("{}", value)) assert_eq!("\"Hello\"", format!("{}", value))
} }
_ => panic!(), _ => panic!(),
@ -83,7 +76,7 @@ mod tests {
let expression = try_parse(&tokens, 0); let expression = try_parse(&tokens, 0);
match expression { match expression {
ParseResult::Ok(Expression::Boolean(value), _) => assert!(value), Ok((Expression::Boolean(value), _)) => assert!(value),
_ => panic!(), _ => panic!(),
} }
} }
@ -94,7 +87,7 @@ mod tests {
let expression = try_parse(&tokens, 0); let expression = try_parse(&tokens, 0);
match expression { match expression {
ParseResult::Ok(Expression::Identifier(value), _) => { Ok((Expression::Identifier(value), _)) => {
assert_eq!("someIdentifier", format!("{}", value)) assert_eq!("someIdentifier", format!("{}", value))
} }
_ => panic!(), _ => panic!(),
@ -107,7 +100,7 @@ mod tests {
let expression = try_parse(&tokens, 0); let expression = try_parse(&tokens, 0);
match expression { match expression {
ParseResult::Ok(Expression::Identifier(value), _) => { Ok((Expression::Identifier(value), _)) => {
assert_eq!("identifier", format!("{}", value)) assert_eq!("identifier", format!("{}", value))
} }
_ => panic!(), _ => panic!(),

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
lexic::token::Token, lexic::token::Token,
syntax::{ast::Expression, ParseResult}, syntax::{ast::Expression, ParsingError, ParsingResult},
}; };
/// Parses a factor expression. /// Parses a factor expression.
@ -8,37 +8,37 @@ use crate::{
/// ```ebnf /// ```ebnf
/// term = factor, (("-" | "+"), factor)*; /// term = factor, (("-" | "+"), factor)*;
/// ``` /// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> { pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
let (factor, next_pos) = match super::factor::try_parse(tokens, pos) { let (factor, next_pos) = match super::factor::try_parse(tokens, pos) {
ParseResult::Ok(expr, next_pos) => (expr, next_pos), Ok((expr, next_pos)) => (expr, next_pos),
_ => return ParseResult::Unmatched, _ => return Err(ParsingError::Unmatched),
}; };
parse_many(tokens, next_pos, factor) parse_many(tokens, next_pos, factor)
} }
fn parse_many( fn parse_many<'a>(
tokens: &Vec<Token>, tokens: &'a Vec<Token>,
pos: usize, pos: usize,
prev_expr: Expression, prev_expr: Expression<'a>,
) -> ParseResult<Expression, ()> { ) -> ParsingResult<'a, Expression<'a>> {
// term = factor, (("-" | "+"), factor)*; // term = factor, (("-" | "+"), factor)*;
match tokens.get(pos) { match tokens.get(pos) {
Some(token) if token.value == "+" || token.value == "-" => { Some(token) if token.value == "+" || token.value == "-" => {
match super::factor::try_parse(tokens, pos + 1) { match super::factor::try_parse(tokens, pos + 1) {
ParseResult::Ok(expr, next_pos) => { Ok((expr, next_pos)) => {
let expr = Expression::BinaryOperator( let expr = Expression::BinaryOperator(
Box::new(prev_expr), Box::new(prev_expr),
Box::new(expr), Box::new(expr),
Box::new(token.value.clone()), &token.value,
); );
parse_many(tokens, next_pos, expr) parse_many(tokens, next_pos, expr)
} }
_ => ParseResult::Unmatched, _ => Err(ParsingError::Unmatched),
} }
} }
_ => ParseResult::Ok(prev_expr, pos), _ => Ok((prev_expr, pos)),
} }
} }

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
lexic::token::Token, lexic::token::Token,
syntax::{ast::Expression, ParseResult}, syntax::{ast::Expression, ParsingError, ParsingResult},
}; };
use super::function_call_expr; use super::function_call_expr;
@ -11,15 +11,15 @@ use super::function_call_expr;
/// unary = ("!" | "-"), expression /// unary = ("!" | "-"), expression
/// | function call expr; /// | function call expr;
/// ``` /// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> { pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
match tokens.get(pos) { match tokens.get(pos) {
Some(token) if token.value == "!" || token.value == "-" => { Some(token) if token.value == "!" || token.value == "-" => {
match super::try_parse(tokens, pos + 1) { match super::try_parse(tokens, pos + 1) {
ParseResult::Ok(expression, next_pos) => ParseResult::Ok( Ok((expression, next_pos)) => Ok((
Expression::UnaryOperator(Box::new(token.value.clone()), Box::new(expression)), Expression::UnaryOperator(&token.value, Box::new(expression)),
next_pos, next_pos,
), )),
_ => ParseResult::Unmatched, _ => Err(ParsingError::Unmatched),
} }
} }
_ => function_call_expr::try_parse(tokens, pos), _ => function_call_expr::try_parse(tokens, pos),
@ -37,7 +37,7 @@ mod tests {
let expression = try_parse(&tokens, 0); let expression = try_parse(&tokens, 0);
match expression { match expression {
ParseResult::Ok(Expression::Identifier(value), _) => { Ok((Expression::Identifier(value), _)) => {
assert_eq!("identifier", format!("{}", value)) assert_eq!("identifier", format!("{}", value))
} }
_ => panic!(), _ => panic!(),
@ -50,7 +50,7 @@ mod tests {
let expression = try_parse(&tokens, 0); let expression = try_parse(&tokens, 0);
match expression { match expression {
ParseResult::Ok(Expression::UnaryOperator(operator, expression), _) => { Ok((Expression::UnaryOperator(operator, expression), _)) => {
match (operator, *expression) { match (operator, *expression) {
(op, Expression::Number(value)) => { (op, Expression::Number(value)) => {
assert_eq!(*op, "-"); assert_eq!(*op, "-");
@ -69,7 +69,7 @@ mod tests {
let expression = try_parse(&tokens, 0); let expression = try_parse(&tokens, 0);
match expression { match expression {
ParseResult::Ok(Expression::UnaryOperator(operator, expression), _) => { Ok((Expression::UnaryOperator(operator, expression), _)) => {
assert_eq!(*operator, "-"); assert_eq!(*operator, "-");
match *expression { match *expression {
Expression::BinaryOperator(_, _, _) => { Expression::BinaryOperator(_, _, _) => {

View File

@ -4,19 +4,19 @@ use crate::{
syntax::{ syntax::{
ast::{functions::ArgumentsList, Expression}, ast::{functions::ArgumentsList, Expression},
utils::parse_token_type, utils::parse_token_type,
ParseResult, ParsingError, ParsingResult,
}, },
}; };
pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<ArgumentsList, &Token> { pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParsingResult<ArgumentsList> {
let mut current_pos = pos; let mut current_pos = pos;
let (opening_paren, next_pos) = let (opening_paren, next_pos) =
match parse_token_type(tokens, current_pos, TokenType::LeftParen) { match parse_token_type(tokens, current_pos, TokenType::LeftParen) {
ParseResult::Ok(t, next) => (t, next), Ok((t, next)) => (t, next),
ParseResult::Err(err) => return ParseResult::Err(err), Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
ParseResult::Mismatch(t) => return ParseResult::Mismatch(t), Err(ParsingError::Mismatch(t)) => return Err(ParsingError::Mismatch(t)),
ParseResult::Unmatched => return ParseResult::Unmatched, Err(ParsingError::Unmatched) => return Err(ParsingError::Unmatched),
}; };
current_pos = next_pos; current_pos = next_pos;
@ -24,10 +24,10 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Argument
loop { loop {
let (next_expression, next_pos) = let (next_expression, next_pos) =
match super::super::expression::try_parse(tokens, current_pos) { match super::super::expression::try_parse(tokens, current_pos) {
ParseResult::Ok(expression, next_pos) => (expression, next_pos), Ok((expression, next_pos)) => (expression, next_pos),
ParseResult::Err(error) => { Err(ParsingError::Err(error)) => {
// TODO: Write a more detailed error // TODO: Write a more detailed error
return ParseResult::Err(error); return Err(ParsingError::Err(error));
} }
_ => break, _ => break,
}; };
@ -37,44 +37,44 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Argument
// Parse comma. This also parses a trailing comma // Parse comma. This also parses a trailing comma
match parse_token_type(tokens, current_pos, TokenType::Comma) { match parse_token_type(tokens, current_pos, TokenType::Comma) {
ParseResult::Ok(_, next) => { Ok((_, next)) => {
current_pos = next; current_pos = next;
} }
// This should never happen // This should never happen
ParseResult::Err(err) => return ParseResult::Err(err), Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
ParseResult::Mismatch(_) => { Err(ParsingError::Mismatch(_)) => {
// Something other than a comma was found. It must be a closing paren ) // Something other than a comma was found. It must be a closing paren )
// Still, break the loop, assume there are no more arguments // Still, break the loop, assume there are no more arguments
// TODO: This could be a good place to write a detailed error? // TODO: This could be a good place to write a detailed error?
break; break;
} }
ParseResult::Unmatched => break, Err(ParsingError::Unmatched) => break,
}; };
} }
// Parse closing paren // Parse closing paren
let (_closing_paren, next_pos) = let (_closing_paren, next_pos) =
match parse_token_type(tokens, current_pos, TokenType::RightParen) { match parse_token_type(tokens, current_pos, TokenType::RightParen) {
ParseResult::Ok(t, next) => (t, next), Ok((t, next)) => (t, next),
ParseResult::Err(err) => return ParseResult::Err(err), Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
ParseResult::Mismatch(t) => { Err(ParsingError::Mismatch(t)) => {
return ParseResult::Err(SyntaxError { return Err(ParsingError::Err(SyntaxError {
reason: String::from("Expected a closing paren after the function identifier."), reason: String::from("Expected a closing paren after the function identifier."),
error_start: t.position, error_start: t.position,
error_end: t.get_end_position(), error_end: t.get_end_position(),
}); }));
} }
ParseResult::Unmatched => { Err(ParsingError::Unmatched) => {
return ParseResult::Err(SyntaxError { return Err(ParsingError::Err(SyntaxError {
reason: String::from("Expected a closing paren after the function identifier."), reason: String::from("Expected a closing paren after the function identifier."),
error_start: opening_paren.position, error_start: opening_paren.position,
error_end: opening_paren.get_end_position(), error_end: opening_paren.get_end_position(),
}); }));
} }
}; };
current_pos = next_pos; current_pos = next_pos;
ParseResult::Ok(ArgumentsList { arguments }, current_pos) Ok((ArgumentsList { arguments }, current_pos))
} }
#[cfg(test)] #[cfg(test)]
@ -87,7 +87,7 @@ mod tests {
let tokens = get_tokens(&String::from("()")).unwrap(); let tokens = get_tokens(&String::from("()")).unwrap();
let fun_decl = try_parse(&tokens, 0); let fun_decl = try_parse(&tokens, 0);
let ParseResult::Ok(list, next) = fun_decl else { let Ok((list, next)) = fun_decl else {
panic!("Expected an unmatched result: {:?}", fun_decl); panic!("Expected an unmatched result: {:?}", fun_decl);
}; };
@ -100,7 +100,7 @@ mod tests {
let tokens = get_tokens(&String::from("( ) ")).unwrap(); let tokens = get_tokens(&String::from("( ) ")).unwrap();
let fun_decl = try_parse(&tokens, 0); let fun_decl = try_parse(&tokens, 0);
let ParseResult::Ok(list, next) = fun_decl else { let Ok((list, next)) = fun_decl else {
panic!("Expected a result, got: {:?}", fun_decl); panic!("Expected a result, got: {:?}", fun_decl);
}; };
@ -113,7 +113,7 @@ mod tests {
let tokens = get_tokens(&String::from("(\n \n)")).unwrap(); let tokens = get_tokens(&String::from("(\n \n)")).unwrap();
let fun_decl = try_parse(&tokens, 0); let fun_decl = try_parse(&tokens, 0);
let ParseResult::Ok(list, next) = fun_decl else { let Ok((list, next)) = fun_decl else {
panic!("Expected a result, got: {:?}", fun_decl); panic!("Expected a result, got: {:?}", fun_decl);
}; };
@ -126,7 +126,7 @@ mod tests {
let tokens = get_tokens(&String::from("(0)")).unwrap(); let tokens = get_tokens(&String::from("(0)")).unwrap();
let fun_decl = try_parse(&tokens, 0); let fun_decl = try_parse(&tokens, 0);
let ParseResult::Ok(arguments_list, next) = fun_decl else { let Ok((arguments_list, next)) = fun_decl else {
panic!("Expected a result, got: {:?}", fun_decl); panic!("Expected a result, got: {:?}", fun_decl);
}; };
@ -145,7 +145,7 @@ mod tests {
let tokens = get_tokens(&String::from("(0, )")).unwrap(); let tokens = get_tokens(&String::from("(0, )")).unwrap();
let fun_decl = try_parse(&tokens, 0); let fun_decl = try_parse(&tokens, 0);
let ParseResult::Ok(arguments_list, next) = fun_decl else { let Ok((arguments_list, next)) = fun_decl else {
panic!("Expected a result, got: {:?}", fun_decl); panic!("Expected a result, got: {:?}", fun_decl);
}; };
@ -163,7 +163,7 @@ mod tests {
let tokens = get_tokens(&String::from("(\"Hello new world\", 322, )")).unwrap(); let tokens = get_tokens(&String::from("(\"Hello new world\", 322, )")).unwrap();
let fun_decl = try_parse(&tokens, 0); let fun_decl = try_parse(&tokens, 0);
let ParseResult::Ok(arguments_list, _next) = fun_decl else { let Ok((arguments_list, _next)) = fun_decl else {
panic!("Expected a result, got: {:?}", fun_decl); panic!("Expected a result, got: {:?}", fun_decl);
}; };
@ -185,7 +185,7 @@ mod tests {
let tokens = get_tokens(&String::from("(foo(), bar())")).unwrap(); let tokens = get_tokens(&String::from("(foo(), bar())")).unwrap();
let fun_decl = try_parse(&tokens, 0); let fun_decl = try_parse(&tokens, 0);
let ParseResult::Ok(arguments_list, _next) = fun_decl else { let Ok((arguments_list, _next)) = fun_decl else {
panic!("Expected a result, got: {:?}", fun_decl); panic!("Expected a result, got: {:?}", fun_decl);
}; };

View File

@ -1,102 +1,96 @@
use crate::{ use crate::{
error_handling::SyntaxError, error_handling::SyntaxError,
lexic::token::{Token, TokenType}, lexic::token::{Token, TokenType},
utils::Result3, syntax::{ParsingError, ParsingResult},
}; };
use super::{ use super::{
super::{ super::{ast::FunctionDeclaration, block::parse_block, utils::parse_token_type},
ast::FunctionDeclaration,
block::parse_block,
utils::{parse_token_type, try_token_type},
ParseResult,
},
params_list::parse_params_list, params_list::parse_params_list,
}; };
pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<FunctionDeclaration, ()> { pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParsingResult<FunctionDeclaration> {
let mut current_pos = pos; let mut current_pos = pos;
// `fun` keyword // `fun` keyword
let fun_keyword = match try_token_type(tokens, current_pos, TokenType::FUN) { let (fun_keyword, next_pos) = match parse_token_type(tokens, current_pos, TokenType::FUN) {
Result3::Ok(t) => t, Ok((t, next)) => (t, next),
Result3::Err(_token) => return ParseResult::Unmatched, _ => return Err(ParsingError::Unmatched),
Result3::None => return ParseResult::Unmatched,
}; };
current_pos += 1; current_pos = next_pos;
let (identifier, next_pos) = match parse_token_type(tokens, current_pos, TokenType::Identifier) let (identifier, next_pos) = match parse_token_type(tokens, current_pos, TokenType::Identifier)
{ {
ParseResult::Ok(id, next) => (id, next), Ok((id, next)) => (id, next),
ParseResult::Err(err) => return ParseResult::Err(err), Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
ParseResult::Mismatch(wrong_token) => { Err(ParsingError::Mismatch(wrong_token)) => {
return ParseResult::Err(SyntaxError { return Err(ParsingError::Err(SyntaxError {
reason: String::from("Expected an identifier after the `fun` keyword."), reason: String::from("Expected an identifier after the `fun` keyword."),
error_start: wrong_token.position, error_start: wrong_token.position,
error_end: wrong_token.get_end_position(), error_end: wrong_token.get_end_position(),
}); }));
} }
ParseResult::Unmatched => { Err(ParsingError::Unmatched) => {
return ParseResult::Err(SyntaxError { return Err(ParsingError::Err(SyntaxError {
reason: String::from("Expected an identifier after the `fun` keyword."), reason: String::from("Expected an identifier after the `fun` keyword."),
error_start: fun_keyword.position, error_start: fun_keyword.position,
error_end: fun_keyword.get_end_position(), error_end: fun_keyword.get_end_position(),
}); }));
} }
}; };
current_pos = next_pos; current_pos = next_pos;
let (params_list, next_pos) = match parse_params_list(tokens, current_pos) { let (params_list, next_pos) = match parse_params_list(tokens, current_pos) {
ParseResult::Ok(params, next_pos) => (params, next_pos), Ok((params, next_pos)) => (params, next_pos),
ParseResult::Err(err) => return ParseResult::Err(err), Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
ParseResult::Mismatch(wrong_token) => { Err(ParsingError::Mismatch(wrong_token)) => {
return ParseResult::Err(SyntaxError { return Err(ParsingError::Err(SyntaxError {
reason: String::from("Expected an opening paren afted the function identifier."), reason: String::from("Expected an opening paren afted the function identifier."),
error_start: wrong_token.position, error_start: wrong_token.position,
error_end: wrong_token.get_end_position(), error_end: wrong_token.get_end_position(),
}); }));
} }
ParseResult::Unmatched => { Err(ParsingError::Unmatched) => {
return ParseResult::Err(SyntaxError { return Err(ParsingError::Err(SyntaxError {
reason: String::from("Expected an opening paren afted the function identifier."), reason: String::from("Expected an opening paren afted the function identifier."),
error_start: identifier.position, error_start: identifier.position,
error_end: identifier.get_end_position(), error_end: identifier.get_end_position(),
}); }));
} }
}; };
current_pos = next_pos; current_pos = next_pos;
let (block, next_pos) = match parse_block(tokens, current_pos) { let (block, next_pos) = match parse_block(tokens, current_pos) {
ParseResult::Ok(block, next_pos) => (block, next_pos), Ok((block, next_pos)) => (block, next_pos),
ParseResult::Err(error) => { Err(ParsingError::Err(error)) => {
return ParseResult::Err(error); return Err(ParsingError::Err(error));
} }
ParseResult::Mismatch(wrong_token) => { Err(ParsingError::Mismatch(wrong_token)) => {
return ParseResult::Err(SyntaxError { return Err(ParsingError::Err(SyntaxError {
reason: String::from("Expected a block after the function declaration."), reason: String::from("Expected a block after the function declaration."),
error_start: wrong_token.position, error_start: wrong_token.position,
error_end: wrong_token.get_end_position(), error_end: wrong_token.get_end_position(),
}); }));
} }
ParseResult::Unmatched => { Err(ParsingError::Unmatched) => {
return ParseResult::Err(SyntaxError { return Err(ParsingError::Err(SyntaxError {
reason: String::from("Expected a block after the function declaration."), reason: String::from("Expected a block after the function declaration."),
error_start: identifier.position, error_start: identifier.position,
error_end: identifier.get_end_position(), error_end: identifier.get_end_position(),
}); }));
} }
}; };
current_pos = next_pos; current_pos = next_pos;
// Construct and return the function declaration // Construct and return the function declaration
ParseResult::Ok( Ok((
FunctionDeclaration { FunctionDeclaration {
identifier: Box::new(identifier.value.clone()), identifier: &identifier,
params_list: Box::new(params_list), params_list: Box::new(params_list),
block: Box::new(block), block: Box::new(block),
}, },
current_pos, current_pos,
) ))
} }
#[cfg(test)] #[cfg(test)]
@ -110,7 +104,7 @@ mod tests {
let tokens = get_tokens(&String::from("val identifier = 20")).unwrap(); let tokens = get_tokens(&String::from("val identifier = 20")).unwrap();
let fun_decl = try_parse(&tokens, 0); let fun_decl = try_parse(&tokens, 0);
let ParseResult::Unmatched = fun_decl else { let Err(ParsingError::Unmatched) = fun_decl else {
panic!("Expected an unmatched result: {:?}", fun_decl); panic!("Expected an unmatched result: {:?}", fun_decl);
}; };
} }
@ -121,7 +115,7 @@ mod tests {
let fun_decl = try_parse(&tokens, 0); let fun_decl = try_parse(&tokens, 0);
match fun_decl { match fun_decl {
ParseResult::Err(err) => { Err(ParsingError::Err(err)) => {
assert_eq!( assert_eq!(
err.reason, err.reason,
"Expected an identifier after the `fun` keyword." "Expected an identifier after the `fun` keyword."
@ -135,7 +129,7 @@ mod tests {
let tokens = get_tokens(&String::from("fun")).unwrap(); let tokens = get_tokens(&String::from("fun")).unwrap();
let fun_decl = try_parse(&tokens, 0); let fun_decl = try_parse(&tokens, 0);
match fun_decl { match fun_decl {
ParseResult::Err(err) => { Err(ParsingError::Err(err)) => {
assert_eq!( assert_eq!(
err.reason, err.reason,
"Expected an identifier after the `fun` keyword." "Expected an identifier after the `fun` keyword."
@ -153,7 +147,7 @@ mod tests {
let fun_decl = try_parse(&tokens, 0); let fun_decl = try_parse(&tokens, 0);
match fun_decl { match fun_decl {
ParseResult::Err(err) => { Err(ParsingError::Err(err)) => {
assert_eq!( assert_eq!(
err.reason, err.reason,
"Expected an opening paren afted the function identifier." "Expected an opening paren afted the function identifier."
@ -167,7 +161,7 @@ mod tests {
let tokens = get_tokens(&String::from("fun id")).unwrap(); let tokens = get_tokens(&String::from("fun id")).unwrap();
let fun_decl = try_parse(&tokens, 0); let fun_decl = try_parse(&tokens, 0);
match fun_decl { match fun_decl {
ParseResult::Err(err) => { Err(ParsingError::Err(err)) => {
assert_eq!( assert_eq!(
err.reason, err.reason,
"Expected an opening paren afted the function identifier." "Expected an opening paren afted the function identifier."
@ -185,7 +179,7 @@ mod tests {
let fun_decl = try_parse(&tokens, 0); let fun_decl = try_parse(&tokens, 0);
match fun_decl { match fun_decl {
ParseResult::Err(err) => { Err(ParsingError::Err(err)) => {
assert_eq!( assert_eq!(
err.reason, err.reason,
"Expected a closing paren after the function identifier." "Expected a closing paren after the function identifier."
@ -199,7 +193,7 @@ mod tests {
let tokens = get_tokens(&String::from("fun id(")).unwrap(); let tokens = get_tokens(&String::from("fun id(")).unwrap();
let fun_decl = try_parse(&tokens, 0); let fun_decl = try_parse(&tokens, 0);
match fun_decl { match fun_decl {
ParseResult::Err(err) => { Err(ParsingError::Err(err)) => {
assert_eq!( assert_eq!(
err.reason, err.reason,
"Expected a closing paren after the function identifier." "Expected a closing paren after the function identifier."
@ -217,7 +211,7 @@ mod tests {
let fun_decl = try_parse(&tokens, 0); let fun_decl = try_parse(&tokens, 0);
match fun_decl { match fun_decl {
ParseResult::Err(err) => { Err(ParsingError::Err(err)) => {
assert_eq!( assert_eq!(
err.reason, err.reason,
"Expected an identifier after the `fun` keyword." "Expected an identifier after the `fun` keyword."
@ -233,7 +227,7 @@ mod tests {
let fun_decl = try_parse(&tokens, 0); let fun_decl = try_parse(&tokens, 0);
match fun_decl { match fun_decl {
ParseResult::Err(err) => { Err(ParsingError::Err(err)) => {
assert_eq!( assert_eq!(
err.reason, err.reason,
"Expected an identifier after the `fun` keyword." "Expected an identifier after the `fun` keyword."
@ -251,7 +245,7 @@ mod tests {
let fun_decl = try_parse(&tokens, 0); let fun_decl = try_parse(&tokens, 0);
match fun_decl { match fun_decl {
ParseResult::Err(err) => { Err(ParsingError::Err(err)) => {
assert_eq!( assert_eq!(
err.reason, err.reason,
"Expected a block after the function declaration." "Expected a block after the function declaration."
@ -265,7 +259,7 @@ mod tests {
let tokens = get_tokens(&String::from("fun id()")).unwrap(); let tokens = get_tokens(&String::from("fun id()")).unwrap();
let fun_decl = try_parse(&tokens, 0); let fun_decl = try_parse(&tokens, 0);
match fun_decl { match fun_decl {
ParseResult::Err(err) => { Err(ParsingError::Err(err)) => {
assert_eq!( assert_eq!(
err.reason, err.reason,
"Expected a block after the function declaration." "Expected a block after the function declaration."
@ -283,7 +277,7 @@ mod tests {
let fun_decl = try_parse(&tokens, 0); let fun_decl = try_parse(&tokens, 0);
match fun_decl { match fun_decl {
ParseResult::Err(err) => { Err(ParsingError::Err(err)) => {
assert_eq!(err.reason, "Expected a closing brace after the block body."); assert_eq!(err.reason, "Expected a closing brace after the block body.");
assert_eq!(err.error_start, 11); assert_eq!(err.error_start, 11);
assert_eq!(err.error_end, 13); assert_eq!(err.error_end, 13);
@ -295,7 +289,7 @@ mod tests {
let fun_decl = try_parse(&tokens, 0); let fun_decl = try_parse(&tokens, 0);
match fun_decl { match fun_decl {
ParseResult::Err(err) => { Err(ParsingError::Err(err)) => {
assert_eq!(err.reason, "Expected a closing brace after the block body."); assert_eq!(err.reason, "Expected a closing brace after the block body.");
assert_eq!(err.error_start, 9); assert_eq!(err.error_start, 9);
assert_eq!(err.error_end, 10); assert_eq!(err.error_end, 10);
@ -307,14 +301,9 @@ mod tests {
#[test] #[test]
fn should_parse_simple_function_declaration() { fn should_parse_simple_function_declaration() {
let tokens = get_tokens(&String::from("fun id() {}")).unwrap(); let tokens = get_tokens(&String::from("fun id() {}")).unwrap();
let ParseResult::Ok(function_declaration, _) = try_parse(&tokens, 0) else { let (function_declaration, _) = try_parse(&tokens, 0).unwrap();
panic!("Expected a function declaration.")
};
assert_eq!( assert_eq!(function_declaration.identifier.value, String::from("id"));
function_declaration.identifier,
Box::new(String::from("id"))
);
} }
} }
@ -327,48 +316,38 @@ mod whitespace_test {
#[test] #[test]
fn should_ignore_whitespace_1() { fn should_ignore_whitespace_1() {
let tokens = get_tokens(&String::from("fun\nid() {}")).unwrap(); let tokens = get_tokens(&String::from("fun\nid() {}")).unwrap();
let ParseResult::Ok(declaration, _) = try_parse(&tokens, 0) else { let (declaration, _) = try_parse(&tokens, 0).unwrap();
panic!("Expected a function declaration.")
};
assert_eq!(declaration.identifier, Box::new(String::from("id"))); assert_eq!(declaration.identifier.value, (String::from("id")));
} }
#[test] #[test]
fn should_ignore_whitespace_2() { fn should_ignore_whitespace_2() {
let tokens = get_tokens(&String::from("fun\nid\n() {}")).unwrap(); let tokens = get_tokens(&String::from("fun\nid\n() {}")).unwrap();
let ParseResult::Ok(declaration, _) = try_parse(&tokens, 0) else { let (declaration, _) = try_parse(&tokens, 0).unwrap();
panic!("Expected a function declaration.")
};
assert_eq!(declaration.identifier, Box::new(String::from("id"))); assert_eq!(declaration.identifier.value, (String::from("id")));
} }
#[test] #[test]
fn should_ignore_whitespace_3() { fn should_ignore_whitespace_3() {
let tokens = get_tokens(&String::from("fun\nid\n(\n) {}")).unwrap(); let tokens = get_tokens(&String::from("fun\nid\n(\n) {}")).unwrap();
let ParseResult::Ok(declaration, _) = try_parse(&tokens, 0) else { let (declaration, _) = try_parse(&tokens, 0).unwrap();
panic!("Expected a function declaration.")
};
assert_eq!(declaration.identifier, Box::new(String::from("id"))); assert_eq!(declaration.identifier.value, (String::from("id")));
} }
#[test] #[test]
fn should_ignore_whitespace_4() { fn should_ignore_whitespace_4() {
let tokens = get_tokens(&String::from("fun id\n(\n)\n{}")).unwrap(); let tokens = get_tokens(&String::from("fun id\n(\n)\n{}")).unwrap();
let ParseResult::Ok(declaration, _) = try_parse(&tokens, 0) else { let (declaration, _) = try_parse(&tokens, 0).unwrap();
panic!("Expected a function declaration.") assert_eq!(declaration.identifier.value, (String::from("id")));
};
assert_eq!(declaration.identifier, Box::new(String::from("id")));
} }
#[test] #[test]
fn should_ignore_whitespace_5() { fn should_ignore_whitespace_5() {
let tokens = get_tokens(&String::from("fun\nid() \n{\n}")).unwrap(); let tokens = get_tokens(&String::from("fun\nid() \n{\n}")).unwrap();
let ParseResult::Ok(declaration, _) = try_parse(&tokens, 0) else { let (declaration, _) = try_parse(&tokens, 0).unwrap();
panic!("Expected a function declaration.") assert_eq!(declaration.identifier.value, (String::from("id")));
};
assert_eq!(declaration.identifier, Box::new(String::from("id")));
} }
} }

View File

@ -1,36 +1,41 @@
use crate::{ use crate::{
error_handling::SyntaxError, error_handling::SyntaxError,
lexic::token::{Token, TokenType}, lexic::token::{Token, TokenType},
syntax::utils::parse_token_type, syntax::{utils::parse_token_type, ParsingError, ParsingResult},
}; };
use super::super::{ use super::super::{
ast::{Parameter, ParamsList}, ast::{Parameter, ParamsList},
utils, ParseResult, utils,
}; };
pub fn parse_params_list<'a>( pub fn parse_params_list<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParsingResult<ParamsList> {
tokens: &'a Vec<Token>,
pos: usize,
) -> ParseResult<ParamsList, &Token> {
let mut current_pos = pos; let mut current_pos = pos;
let (opening_paren, next_pos) = let (opening_paren, next_pos) =
match parse_token_type(tokens, current_pos, TokenType::LeftParen) { match parse_token_type(tokens, current_pos, TokenType::LeftParen) {
ParseResult::Ok(t, next) => (t, next), Ok((t, next)) => (t, next),
ParseResult::Err(err) => return ParseResult::Err(err), Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
ParseResult::Mismatch(t) => return ParseResult::Mismatch(t), Err(ParsingError::Mismatch(t)) => return Err(ParsingError::Mismatch(&t)),
ParseResult::Unmatched => return ParseResult::Unmatched, Err(ParsingError::Unmatched) => return Err(ParsingError::Unmatched),
}; };
current_pos = next_pos; current_pos = next_pos;
/*
val (opening_paren, next_pos) = try parse_token_type(...)
val (next_parameter, next_pos) = try parse_param_definition(...) catch
case ::Err(e) { return ::Err(e) }
else { break }
*/
// Parse parameters definitions, separated by commas // Parse parameters definitions, separated by commas
let mut parameters = Vec::<Parameter>::new(); let mut parameters = Vec::<Parameter>::new();
loop { loop {
let (next_parameter, next_pos) = match parse_param_definition(tokens, current_pos) { let (next_parameter, next_pos) = match parse_param_definition(tokens, current_pos) {
ParseResult::Ok(parameter, next_pos) => (parameter, next_pos), Ok((parameter, next_pos)) => (parameter, next_pos),
ParseResult::Err(error) => { Err(ParsingError::Err(error)) => {
return ParseResult::Err(error); return Err(ParsingError::Err(error));
} }
_ => break, _ => break,
}; };
@ -39,50 +44,47 @@ pub fn parse_params_list<'a>(
// Parse comma. This also parses a trailing comma // Parse comma. This also parses a trailing comma
match parse_token_type(tokens, current_pos, TokenType::Comma) { match parse_token_type(tokens, current_pos, TokenType::Comma) {
ParseResult::Ok(_, next) => { Ok((_, next)) => {
current_pos = next; current_pos = next;
} }
// This should never happen // This should never happen
ParseResult::Err(err) => return ParseResult::Err(err), Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
ParseResult::Mismatch(_) => { Err(ParsingError::Mismatch(_)) => {
// Something other than a comma was found. It must be a closing paren ) // Something other than a comma was found. It must be a closing paren )
// Still, break the loop, assume there are no more arguments // Still, break the loop, assume there are no more arguments
// TODO: This could be a good place to write a detailed error? // TODO: This could be a good place to write a detailed error?
break; break;
} }
ParseResult::Unmatched => break, Err(ParsingError::Unmatched) => break,
}; };
} }
// Parse closing paren // Parse closing paren
let (_closing_paren, next_pos) = let (_closing_paren, next_pos) =
match parse_token_type(tokens, current_pos, TokenType::RightParen) { match parse_token_type(tokens, current_pos, TokenType::RightParen) {
ParseResult::Ok(t, next) => (t, next), Ok((t, next)) => (t, next),
ParseResult::Err(err) => return ParseResult::Err(err), Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
ParseResult::Mismatch(t) => { Err(ParsingError::Mismatch(t)) => {
return ParseResult::Err(SyntaxError { return Err(ParsingError::Err(SyntaxError {
reason: String::from("Expected a closing paren after the function identifier."), reason: String::from("Expected a closing paren after the function identifier."),
error_start: t.position, error_start: t.position,
error_end: t.get_end_position(), error_end: t.get_end_position(),
}); }));
} }
ParseResult::Unmatched => { Err(ParsingError::Unmatched) => {
return ParseResult::Err(SyntaxError { return Err(ParsingError::Err(SyntaxError {
reason: String::from("Expected a closing paren after the function identifier."), reason: String::from("Expected a closing paren after the function identifier."),
error_start: opening_paren.position, error_start: opening_paren.position,
error_end: opening_paren.get_end_position(), error_end: opening_paren.get_end_position(),
}); }));
} }
}; };
current_pos = next_pos; current_pos = next_pos;
ParseResult::Ok(ParamsList {}, current_pos) Ok((ParamsList {}, current_pos))
} }
fn parse_param_definition<'a>( fn parse_param_definition<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParsingResult<Parameter> {
tokens: &'a Vec<Token>,
pos: usize,
) -> ParseResult<Parameter, &Token> {
// Parse a single parameter definition of the form: // Parse a single parameter definition of the form:
// - Type identifier // - Type identifier
// There will be more constructs in the future, like: // There will be more constructs in the future, like:
@ -93,45 +95,45 @@ fn parse_param_definition<'a>(
let mut current_pos = pos; let mut current_pos = pos;
let (datatype, next_pos) = let (datatype, next_pos) =
match utils::parse_token_type(tokens, current_pos, TokenType::Datatype) { match utils::parse_token_type(tokens, current_pos, TokenType::Datatype) {
ParseResult::Ok(token, next) => (token, next), Ok((token, next)) => (token, next),
ParseResult::Err(err) => { Err(ParsingError::Err(err)) => {
return ParseResult::Err(err); return Err(ParsingError::Err(err));
} }
// If there is no datatype this construction doesn't apply. // If there is no datatype this construction doesn't apply.
// Return a mismatch and let the caller handle it // Return a mismatch and let the caller handle it
ParseResult::Mismatch(t) => return ParseResult::Mismatch(t), Err(ParsingError::Mismatch(t)) => return Err(ParsingError::Mismatch(t)),
ParseResult::Unmatched => return ParseResult::Unmatched, Err(ParsingError::Unmatched) => return Err(ParsingError::Unmatched),
}; };
current_pos = next_pos; current_pos = next_pos;
let (identifier, next_pos) = let (identifier, next_pos) =
match utils::parse_token_type(tokens, current_pos, TokenType::Identifier) { match utils::parse_token_type(tokens, current_pos, TokenType::Identifier) {
ParseResult::Ok(token, next) => (token, next), Ok((token, next)) => (token, next),
ParseResult::Err(err) => { Err(ParsingError::Err(err)) => {
return ParseResult::Err(err); return Err(ParsingError::Err(err));
} }
// However, if we fail to parse an identifier, it's an error // However, if we fail to parse an identifier, it's an error
ParseResult::Mismatch(_) => { Err(ParsingError::Mismatch(_)) => {
return ParseResult::Err(SyntaxError { return Err(ParsingError::Err(SyntaxError {
reason: String::from("Expected an identifier for the parameter."), reason: String::from("Expected an identifier for the parameter."),
error_start: tokens[pos].position, error_start: tokens[pos].position,
error_end: tokens[pos].get_end_position(), error_end: tokens[pos].get_end_position(),
}); }));
} }
ParseResult::Unmatched => { Err(ParsingError::Unmatched) => {
return ParseResult::Err(SyntaxError { return Err(ParsingError::Err(SyntaxError {
reason: String::from("Expected an identifier for the parameter."), reason: String::from("Expected an identifier for the parameter."),
error_start: tokens[pos].position, error_start: tokens[pos].position,
error_end: tokens[pos].get_end_position(), error_end: tokens[pos].get_end_position(),
}) }))
} }
}; };
ParseResult::Ok( Ok((
Parameter { Parameter {
identifier: Box::new(identifier.value.clone()), identifier: &identifier.value,
datatype: Box::new(datatype.value.clone()), datatype: &datatype.value,
}, },
next_pos, next_pos,
) ))
} }

View File

@ -1,19 +1,21 @@
# Grammar # Grammar
## Module ## Source file
A module is (commonly) a single source file.
```ebnf ```ebnf
module = top level declaration* source file = top level statement*
``` ```
## Top level declaration ## Top level statement
Current focus: Have a mvp compiler (w lexical/syntactic/semantic analysis + codegen) for
simple function calls, and then implement other features top down
```ebnf ```ebnf
top level declaration = function declaration top level statement = expression
| function declaration
``` ```

View File

@ -14,21 +14,19 @@ use ast::ModuleAST;
use self::ast::TopLevelDeclaration; use self::ast::TopLevelDeclaration;
pub type ParsingResult<'a, A> = Result<(A, usize), ParsingError<'a>>;
#[derive(Debug)] #[derive(Debug)]
pub enum ParseResult<A, B> { pub enum ParsingError<'a> {
/// The parsing was a success. The first element is the parsed construct, /// Some other token was found than the expected one
/// the second element is the position of the next token to parse Mismatch(&'a Token),
Ok(A, usize), /// The parsing didn't succeed, but it's not a fatal error
Unmatched,
/// The parsing failed past a point of no return. /// The parsing failed past a point of no return.
/// ///
/// For example, when parsing a function declaration /// For example, when parsing a function declaration
/// the `fun` token is found, but then no identifier /// the `fun` token is found, but then no identifier
Err(SyntaxError), Err(SyntaxError),
/// Some special value was expected, but something else was found.
/// The inside element is the something else found.
Mismatch(B),
/// This parsing didn't succeed, but it's not a fatal error.
Unmatched,
} }
/// Constructs the Misti AST from a vector of tokens /// Constructs the Misti AST from a vector of tokens
@ -46,11 +44,11 @@ pub fn construct_ast<'a>(tokens: &'a Vec<Token>) -> Result<ModuleAST, MistiError
} }
match next_construct(tokens, current_pos) { match next_construct(tokens, current_pos) {
ParseResult::Ok(module, next_pos) => { Ok((module, next_pos)) => {
top_level_declarations.push(module); top_level_declarations.push(module);
current_pos = next_pos; current_pos = next_pos;
} }
ParseResult::Err(err) => return Err(MistiError::Syntax(err)), Err(ParsingError::Err(err)) => return Err(MistiError::Syntax(err)),
_ => { _ => {
return Err(MistiError::Syntax(SyntaxError { return Err(MistiError::Syntax(SyntaxError {
reason: String::from("PARSER couldn't parse any construction"), reason: String::from("PARSER couldn't parse any construction"),
@ -70,18 +68,21 @@ pub fn construct_ast<'a>(tokens: &'a Vec<Token>) -> Result<ModuleAST, MistiError
fn next_construct<'a>( fn next_construct<'a>(
tokens: &'a Vec<Token>, tokens: &'a Vec<Token>,
current_pos: usize, current_pos: usize,
) -> ParseResult<TopLevelDeclaration, ()> { ) -> ParsingResult<TopLevelDeclaration> {
None.or_else( // Try to parse a function declaration
|| match functions::function_declaration::try_parse(tokens, current_pos) { match functions::function_declaration::try_parse(tokens, current_pos) {
ParseResult::Ok(declaration, next_pos) => Some(ParseResult::Ok( Ok((declaration, next_pos)) => {
return Ok((
TopLevelDeclaration::FunctionDeclaration(declaration), TopLevelDeclaration::FunctionDeclaration(declaration),
next_pos, next_pos,
)), ))
ParseResult::Err(err) => Some(ParseResult::Err(err)), }
_ => None, Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
}, _ => {}
) }
.unwrap_or_else(|| ParseResult::Unmatched)
// No top level construct was found, return unmatched
Err(ParsingError::Unmatched)
} }
#[cfg(test)] #[cfg(test)]
@ -90,7 +91,7 @@ mod tests {
#[test] #[test]
fn should_parse_top_level_construct_with_trailing_newline() { fn should_parse_top_level_construct_with_trailing_newline() {
let input = String::from("fun f1(){}\n"); let input = String::from(" fun f1(){}\n");
let tokens = crate::lexic::get_tokens(&input).unwrap(); let tokens = crate::lexic::get_tokens(&input).unwrap();
let declarations = construct_ast(&tokens).unwrap().declarations; let declarations = construct_ast(&tokens).unwrap().declarations;
@ -101,6 +102,7 @@ mod tests {
TopLevelDeclaration::FunctionDeclaration(_f) => { TopLevelDeclaration::FunctionDeclaration(_f) => {
assert!(true) assert!(true)
} }
_ => panic!("Not implemented: Expression at top level"),
} }
} }
@ -117,6 +119,7 @@ mod tests {
TopLevelDeclaration::FunctionDeclaration(_f) => { TopLevelDeclaration::FunctionDeclaration(_f) => {
assert!(true) assert!(true)
} }
_ => panic!("Not implemented: Expression at top level"),
} }
match declarations.get(1).unwrap() { match declarations.get(1).unwrap() {
@ -124,6 +127,7 @@ mod tests {
TopLevelDeclaration::FunctionDeclaration(_f) => { TopLevelDeclaration::FunctionDeclaration(_f) => {
assert!(true) assert!(true)
} }
_ => panic!("Not implemented: Expression at top level"),
} }
} }
} }

View File

@ -4,26 +4,26 @@ use super::{
ast::{statement::Statement, Expression}, ast::{statement::Statement, Expression},
binding, binding,
expression::function_call_expr, expression::function_call_expr,
ParseResult, ParsingError, ParsingResult,
}; };
pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Statement, ()> { pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParsingResult<Statement> {
None.or_else(|| match binding::try_parse(tokens, pos) { // Try to parse a binding
ParseResult::Ok(b, next) => Some(ParseResult::Ok(Statement::Binding(b), next)), match binding::try_parse(tokens, pos) {
ParseResult::Err(err) => Some(ParseResult::Err(err)), Ok((b, next)) => return Ok((Statement::Binding(b), next)),
_ => None, Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
}) _ => {}
.or_else(|| match function_call_expr::try_parse(tokens, pos) {
ParseResult::Ok(f, next) => {
let Expression::FunctionCall(f) = f else {
return None;
};
Some(ParseResult::Ok(Statement::FunctionCall(f), next))
} }
ParseResult::Err(err) => Some(ParseResult::Err(err)),
_ => None, // Try to parse a function call
}) match function_call_expr::try_parse(tokens, pos) {
.unwrap_or_else(|| ParseResult::Unmatched) Ok((Expression::FunctionCall(f), next)) => return Ok((Statement::FunctionCall(f), next)),
Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)),
_ => {}
};
// Return unmatched
Err(ParsingError::Unmatched)
} }
#[cfg(test)] #[cfg(test)]
@ -37,7 +37,7 @@ mod tests {
let statement = try_parse(&tokens, 0); let statement = try_parse(&tokens, 0);
let statement = match statement { let statement = match statement {
ParseResult::Ok(s, _) => s, Ok((s, _)) => s,
_ => panic!("Expected a statement"), _ => panic!("Expected a statement"),
}; };
@ -54,7 +54,7 @@ mod tests {
let statement = try_parse(&tokens, 0); let statement = try_parse(&tokens, 0);
let statement = match statement { let statement = match statement {
ParseResult::Ok(s, _) => s, Ok((s, _)) => s,
_ => panic!("Expected a statement"), _ => panic!("Expected a statement"),
}; };

View File

@ -1,9 +1,6 @@
use crate::{ use crate::lexic::token::{Token, TokenType};
lexic::token::{Token, TokenType},
utils::Result3,
};
use super::ParseResult; use super::{ParsingError, ParsingResult};
pub trait Tokenizer { pub trait Tokenizer {
fn get_significant<'a>(&'a self, index: usize) -> Option<(&'a Token, usize)>; fn get_significant<'a>(&'a self, index: usize) -> Option<(&'a Token, usize)>;
@ -33,38 +30,31 @@ impl Tokenizer for Vec<Token> {
} }
} }
/// Expects the token at `pos` to be of type `token_type` /// Expects the token at `pos` to be an operator of value `operator`. Doesn't ignore whitespace or newlines
pub fn try_token_type(tokens: &Vec<Token>, pos: usize, token_type: TokenType) -> Result3<&Token> { pub fn try_operator(tokens: &Vec<Token>, pos: usize, operator: String) -> ParsingResult<&Token> {
match tokens.get(pos) { match tokens.get(pos) {
Some(t) if t.token_type == token_type => Result3::Ok(t), Some(t) if t.token_type == TokenType::Operator && t.value == operator => Ok((t, pos + 1)),
Some(t) if t.token_type == TokenType::EOF || t.token_type == TokenType::NewLine => {
Result3::None
}
Some(t) => Result3::Err(t),
None => Result3::None,
}
}
pub fn try_operator(tokens: &Vec<Token>, pos: usize, operator: String) -> Result3<&Token> {
match tokens.get(pos) {
Some(t) if t.token_type == TokenType::Operator && t.value == operator => Result3::Ok(t),
Some(t) if t.token_type == TokenType::NewLine || t.token_type == TokenType::EOF => { Some(t) if t.token_type == TokenType::NewLine || t.token_type == TokenType::EOF => {
Result3::None Err(ParsingError::Unmatched)
} }
Some(t) => Result3::Err(t), Some(t) => Err(ParsingError::Mismatch(t)),
None => Result3::None, None => Err(ParsingError::Unmatched),
} }
} }
/// Expects the token at `pos` to be of type `token_type` /// Expects the token at `pos` to be of type `token_type`, and returns the token and the next position.
///
/// Ignores all whitespace and newlines.
///
/// Only returns: Ok, Unmatched, Mismatched
pub fn parse_token_type( pub fn parse_token_type(
tokens: &Vec<Token>, tokens: &Vec<Token>,
pos: usize, pos: usize,
token_type: TokenType, token_type: TokenType,
) -> ParseResult<&Token, &Token> { ) -> ParsingResult<&Token> {
let mut current_pos = pos; let mut current_pos = pos;
// Ignore all whitespace and newlines // Ignore all whitespace, newlines and semicolons
while let Some(t) = tokens.get(current_pos) { while let Some(t) = tokens.get(current_pos) {
if t.token_type == TokenType::INDENT if t.token_type == TokenType::INDENT
|| t.token_type == TokenType::DEDENT || t.token_type == TokenType::DEDENT
@ -77,11 +67,11 @@ pub fn parse_token_type(
} }
match tokens.get(current_pos) { match tokens.get(current_pos) {
Some(t) if t.token_type == token_type => ParseResult::Ok(t, current_pos + 1), Some(t) if t.token_type == token_type => Ok((t, current_pos + 1)),
Some(t) if t.token_type == TokenType::EOF || t.token_type == TokenType::NewLine => { Some(t) if t.token_type == TokenType::EOF || t.token_type == TokenType::NewLine => {
ParseResult::Unmatched Err(ParsingError::Unmatched)
} }
Some(t) => ParseResult::Mismatch(t), Some(t) => Err(ParsingError::Mismatch(t)),
None => ParseResult::Unmatched, None => Err(ParsingError::Unmatched),
} }
} }

View File

@ -1,15 +0,0 @@
pub enum Result3<T> {
Ok(T),
Err(T),
None,
}
#[allow(dead_code)]
impl<T> Result3<T> {
pub fn unwrap(&self) -> &T {
match self {
Result3::Ok(t) => t,
_ => panic!(""),
}
}
}