Parse minimal function declaration
This commit is contained in:
parent
c61f88aaaa
commit
5f25bae3e0
@ -3,6 +3,7 @@ use crate::syntax::ast::ModuleAST;
|
|||||||
mod binding;
|
mod binding;
|
||||||
mod expression;
|
mod expression;
|
||||||
mod module_ast;
|
mod module_ast;
|
||||||
|
mod top_level_construct;
|
||||||
|
|
||||||
/// Trait that the AST and its nodes implement to support transformation to PHP
|
/// Trait that the AST and its nodes implement to support transformation to PHP
|
||||||
trait Transpilable {
|
trait Transpilable {
|
||||||
|
@ -18,7 +18,7 @@ impl Transpilable for ModuleAST {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::syntax::ast::{Binding, Expression, ValBinding};
|
use crate::syntax::ast::{Binding, Expression, TopLevelConstruct, ValBinding};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn module_ast_should_transpile() {
|
fn module_ast_should_transpile() {
|
||||||
@ -31,7 +31,7 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let module = ModuleAST {
|
let module = ModuleAST {
|
||||||
bindings: vec![binding],
|
bindings: vec![TopLevelConstruct::Binding(binding)],
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = module.transpile();
|
let result = module.transpile();
|
||||||
|
12
src/codegen/top_level_construct.rs
Normal file
12
src/codegen/top_level_construct.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use crate::syntax::ast::TopLevelConstruct;
|
||||||
|
|
||||||
|
use super::Transpilable;
|
||||||
|
|
||||||
|
impl Transpilable for TopLevelConstruct {
|
||||||
|
fn transpile(&self) -> String {
|
||||||
|
match self {
|
||||||
|
TopLevelConstruct::Binding(binding) => binding.transpile(),
|
||||||
|
TopLevelConstruct::FunctionDeclaration(_) => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ fn str_is_keyword(s: &String) -> Option<TokenType> {
|
|||||||
match s.as_str() {
|
match s.as_str() {
|
||||||
"var" => Some(TokenType::VAR),
|
"var" => Some(TokenType::VAR),
|
||||||
"val" => Some(TokenType::VAL),
|
"val" => Some(TokenType::VAL),
|
||||||
|
"fun" => Some(TokenType::FUN),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ pub enum TokenType {
|
|||||||
VAR,
|
VAR,
|
||||||
VAL,
|
VAL,
|
||||||
EOF,
|
EOF,
|
||||||
|
FUN,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -51,9 +51,7 @@ fn main() {
|
|||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
|
||||||
match &cli.command {
|
match &cli.command {
|
||||||
Some(Commands::C {
|
Some(Commands::C { file: input }) => file::compile_file(input),
|
||||||
file: input,
|
|
||||||
}) => file::compile_file(input),
|
|
||||||
Some(Commands::R {}) => {
|
Some(Commands::R {}) => {
|
||||||
println!("{}", get_copyright());
|
println!("{}", get_copyright());
|
||||||
let _ = repl::run();
|
let _ = repl::run();
|
||||||
|
@ -1,24 +1,39 @@
|
|||||||
pub struct ModuleAST {
|
pub struct ModuleAST {
|
||||||
pub bindings: Vec<Binding>,
|
pub bindings: Vec<TopLevelConstruct>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TopLevelConstruct {
|
||||||
|
Binding(Binding),
|
||||||
|
FunctionDeclaration(FunctionDeclaration),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FunctionDeclaration {
|
||||||
|
pub identifier: Box<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum Binding {
|
pub enum Binding {
|
||||||
Val(ValBinding),
|
Val(ValBinding),
|
||||||
Var(VarBinding),
|
Var(VarBinding),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ValBinding {
|
pub struct ValBinding {
|
||||||
pub datatype: Option<String>,
|
pub datatype: Option<String>,
|
||||||
pub identifier: Box<String>,
|
pub identifier: Box<String>,
|
||||||
pub expression: Expression,
|
pub expression: Expression,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct VarBinding {
|
pub struct VarBinding {
|
||||||
pub datatype: Option<String>,
|
pub datatype: Option<String>,
|
||||||
pub identifier: Box<String>,
|
pub identifier: Box<String>,
|
||||||
pub expression: Expression,
|
pub expression: Expression,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum Expression {
|
pub enum Expression {
|
||||||
Number(Box<String>),
|
Number(Box<String>),
|
||||||
String(Box<String>),
|
String(Box<String>),
|
||||||
|
@ -120,7 +120,9 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> Option<SyntaxResult>
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(SyntaxResult::Ok(binding))
|
Some(SyntaxResult::Ok(super::ast::TopLevelConstruct::Binding(
|
||||||
|
binding,
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expects the token at `pos` to be of type `token_type`
|
/// Expects the token at `pos` to be of type `token_type`
|
||||||
@ -149,7 +151,7 @@ fn try_operator(tokens: &Vec<Token>, pos: usize, operator: String) -> Result3<&T
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::lexic::get_tokens;
|
use crate::{lexic::get_tokens, syntax::ast::TopLevelConstruct};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_parse_val_binding() {
|
fn should_parse_val_binding() {
|
||||||
@ -157,7 +159,7 @@ mod tests {
|
|||||||
let binding = try_parse(&tokens, 0).unwrap();
|
let binding = try_parse(&tokens, 0).unwrap();
|
||||||
|
|
||||||
match binding {
|
match binding {
|
||||||
SyntaxResult::Ok(Binding::Val(binding)) => {
|
SyntaxResult::Ok(TopLevelConstruct::Binding(Binding::Val(binding))) => {
|
||||||
assert_eq!("identifier", format!("{}", binding.identifier));
|
assert_eq!("identifier", format!("{}", binding.identifier));
|
||||||
}
|
}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
@ -195,7 +197,7 @@ mod tests {
|
|||||||
let binding = try_parse(&tokens, 0).unwrap();
|
let binding = try_parse(&tokens, 0).unwrap();
|
||||||
|
|
||||||
match binding {
|
match binding {
|
||||||
SyntaxResult::Ok(Binding::Val(binding)) => {
|
SyntaxResult::Ok(TopLevelConstruct::Binding(Binding::Val(binding))) => {
|
||||||
assert_eq!(Some(String::from("Num")), binding.datatype);
|
assert_eq!(Some(String::from("Num")), binding.datatype);
|
||||||
assert_eq!("identifier", format!("{}", binding.identifier));
|
assert_eq!("identifier", format!("{}", binding.identifier));
|
||||||
}
|
}
|
||||||
@ -206,11 +208,11 @@ mod tests {
|
|||||||
let binding = try_parse(&tokens, 0).unwrap();
|
let binding = try_parse(&tokens, 0).unwrap();
|
||||||
|
|
||||||
match binding {
|
match binding {
|
||||||
SyntaxResult::Ok(Binding::Var(binding)) => {
|
SyntaxResult::Ok(TopLevelConstruct::Binding(Binding::Var(binding))) => {
|
||||||
assert_eq!(Some(String::from("Bool")), binding.datatype);
|
assert_eq!(Some(String::from("Bool")), binding.datatype);
|
||||||
assert_eq!("identifier", format!("{}", binding.identifier));
|
assert_eq!("identifier", format!("{}", binding.identifier));
|
||||||
}
|
}
|
||||||
_ => panic!(),
|
_ => panic!("D: {:?}", binding),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
356
src/syntax/function_declaration.rs
Normal file
356
src/syntax/function_declaration.rs
Normal file
@ -0,0 +1,356 @@
|
|||||||
|
use crate::{
|
||||||
|
error_handling::SyntaxError,
|
||||||
|
lexic::token::{Token, TokenType},
|
||||||
|
utils::Result3,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
ast::{FunctionDeclaration, TopLevelConstruct},
|
||||||
|
utils::try_token_type,
|
||||||
|
SyntaxResult,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> Option<SyntaxResult> {
|
||||||
|
let mut current_pos = pos;
|
||||||
|
|
||||||
|
// `fun` keyword
|
||||||
|
let fun_keyword = match try_token_type(tokens, current_pos, TokenType::FUN) {
|
||||||
|
Result3::Ok(t) => t,
|
||||||
|
Result3::Err(_token) => return None,
|
||||||
|
Result3::None => return None,
|
||||||
|
};
|
||||||
|
current_pos += 1;
|
||||||
|
|
||||||
|
// Parse identifier
|
||||||
|
let identifier = match try_token_type(tokens, current_pos, TokenType::Identifier) {
|
||||||
|
Result3::Ok(t) => t,
|
||||||
|
Result3::Err(t) => {
|
||||||
|
// The parser found a token, but it's not an identifier
|
||||||
|
return Some(SyntaxResult::Err(SyntaxError {
|
||||||
|
reason: format!(
|
||||||
|
"There should be an identifier after a `fun` token, but found `{}`",
|
||||||
|
t.value
|
||||||
|
),
|
||||||
|
error_start: t.position,
|
||||||
|
error_end: t.get_end_position(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
Result3::None => {
|
||||||
|
// The parser didn't find any token
|
||||||
|
return Some(SyntaxResult::Err(SyntaxError {
|
||||||
|
reason: format!(
|
||||||
|
"There should be an identifier after a `fun` token, but found nothing"
|
||||||
|
),
|
||||||
|
error_start: fun_keyword.position,
|
||||||
|
error_end: fun_keyword.get_end_position(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
current_pos += 1;
|
||||||
|
|
||||||
|
// Parse an opening paren
|
||||||
|
let opening_paren = match try_token_type(tokens, current_pos, TokenType::LeftParen) {
|
||||||
|
Result3::Ok(t) => t,
|
||||||
|
Result3::Err(t) => {
|
||||||
|
// The parser found a token, but it's not an opening paren
|
||||||
|
return Some(SyntaxResult::Err(SyntaxError {
|
||||||
|
reason: format!(
|
||||||
|
"There should be an opening paren after the identifier, but found `{}`",
|
||||||
|
t.value
|
||||||
|
),
|
||||||
|
error_start: t.position,
|
||||||
|
error_end: t.get_end_position(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
Result3::None => {
|
||||||
|
// The parser didn't find any token
|
||||||
|
return Some(SyntaxResult::Err(SyntaxError {
|
||||||
|
reason: format!(
|
||||||
|
"There should be an opening paren after the identifier, but found nothing"
|
||||||
|
),
|
||||||
|
error_start: identifier.position,
|
||||||
|
error_end: identifier.get_end_position(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
current_pos += 1;
|
||||||
|
|
||||||
|
// Parse a closing paren
|
||||||
|
let closing_paren = match try_token_type(tokens, current_pos, TokenType::RightParen) {
|
||||||
|
Result3::Ok(t) => t,
|
||||||
|
Result3::Err(t) => {
|
||||||
|
// The parser found a token, but it's not an opening paren
|
||||||
|
return Some(SyntaxResult::Err(SyntaxError {
|
||||||
|
reason: format!(
|
||||||
|
"There should be a closing paren after the parameter list, but found `{}`",
|
||||||
|
t.value
|
||||||
|
),
|
||||||
|
error_start: t.position,
|
||||||
|
error_end: t.get_end_position(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
Result3::None => {
|
||||||
|
// The parser didn't find any token
|
||||||
|
return Some(SyntaxResult::Err(SyntaxError {
|
||||||
|
reason: format!(
|
||||||
|
"There should be a closing paren after the parameter list, but found nothing"
|
||||||
|
),
|
||||||
|
error_start: opening_paren.position,
|
||||||
|
error_end: opening_paren.get_end_position(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
current_pos += 1;
|
||||||
|
|
||||||
|
// Parse opening brace
|
||||||
|
let opening_brace = match try_token_type(tokens, current_pos, TokenType::LeftBrace) {
|
||||||
|
Result3::Ok(t) => t,
|
||||||
|
Result3::Err(t) => {
|
||||||
|
// The parser found a token, but it's not an opening brace
|
||||||
|
return Some(SyntaxResult::Err(SyntaxError {
|
||||||
|
reason: format!(
|
||||||
|
"There should be an opening brace after the parameter list, but found `{}`",
|
||||||
|
t.value
|
||||||
|
),
|
||||||
|
error_start: t.position,
|
||||||
|
error_end: t.get_end_position(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
Result3::None => {
|
||||||
|
// The parser didn't find any token
|
||||||
|
return Some(SyntaxResult::Err(SyntaxError {
|
||||||
|
reason: format!(
|
||||||
|
"There should be an opening brace after the parameter list, but found nothing"
|
||||||
|
),
|
||||||
|
error_start: closing_paren.position,
|
||||||
|
error_end: closing_paren.get_end_position(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
current_pos += 1;
|
||||||
|
|
||||||
|
// Parse closing brace
|
||||||
|
let closing_brace = match try_token_type(tokens, current_pos, TokenType::RightBrace) {
|
||||||
|
Result3::Ok(t) => t,
|
||||||
|
Result3::Err(t) => {
|
||||||
|
// The parser found a token, but it's not an opening brace
|
||||||
|
return Some(SyntaxResult::Err(SyntaxError {
|
||||||
|
reason: format!(
|
||||||
|
"There should be a closing brace after the function body, but found `{}`",
|
||||||
|
t.value
|
||||||
|
),
|
||||||
|
error_start: t.position,
|
||||||
|
error_end: t.get_end_position(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
Result3::None => {
|
||||||
|
// The parser didn't find any token
|
||||||
|
return Some(SyntaxResult::Err(SyntaxError {
|
||||||
|
reason: format!(
|
||||||
|
"There should be a closing brace after the function body, but found nothing"
|
||||||
|
),
|
||||||
|
error_start: opening_brace.position,
|
||||||
|
error_end: opening_brace.get_end_position(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Construct and return the function declaration
|
||||||
|
Some(SyntaxResult::Ok(TopLevelConstruct::FunctionDeclaration(
|
||||||
|
FunctionDeclaration {
|
||||||
|
identifier: Box::new(identifier.value.clone()),
|
||||||
|
},
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{lexic::get_tokens, syntax::ast::TopLevelConstruct};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_return_none_on_wrong_initial_token() {
|
||||||
|
let tokens = get_tokens(&String::from("val identifier = 20")).unwrap();
|
||||||
|
let fun_decl = try_parse(&tokens, 0);
|
||||||
|
|
||||||
|
assert!(fun_decl.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_not_parse_fun_without_identifier() {
|
||||||
|
let tokens = get_tokens(&String::from("fun = 20")).unwrap();
|
||||||
|
let fun_decl = try_parse(&tokens, 0);
|
||||||
|
|
||||||
|
match fun_decl {
|
||||||
|
Some(SyntaxResult::Err(err)) => {
|
||||||
|
assert_eq!(
|
||||||
|
err.reason,
|
||||||
|
"There should be an identifier after a `fun` token, but found `=`"
|
||||||
|
);
|
||||||
|
assert_eq!(err.error_start, 4);
|
||||||
|
assert_eq!(err.error_end, 5);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||||
|
}
|
||||||
|
|
||||||
|
let tokens = get_tokens(&String::from("fun")).unwrap();
|
||||||
|
let fun_decl = try_parse(&tokens, 0);
|
||||||
|
match fun_decl {
|
||||||
|
Some(SyntaxResult::Err(err)) => {
|
||||||
|
assert_eq!(
|
||||||
|
err.reason,
|
||||||
|
"There should be an identifier after a `fun` token, but found nothing"
|
||||||
|
);
|
||||||
|
assert_eq!(err.error_start, 0);
|
||||||
|
assert_eq!(err.error_end, 3);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_not_parse_fun_without_parens() {
|
||||||
|
let tokens = get_tokens(&String::from("fun id =")).unwrap();
|
||||||
|
let fun_decl = try_parse(&tokens, 0);
|
||||||
|
|
||||||
|
match fun_decl {
|
||||||
|
Some(SyntaxResult::Err(err)) => {
|
||||||
|
assert_eq!(
|
||||||
|
err.reason,
|
||||||
|
"There should be an opening paren after the identifier, but found `=`"
|
||||||
|
);
|
||||||
|
assert_eq!(err.error_start, 7);
|
||||||
|
assert_eq!(err.error_end, 8);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||||
|
}
|
||||||
|
|
||||||
|
let tokens = get_tokens(&String::from("fun id")).unwrap();
|
||||||
|
let fun_decl = try_parse(&tokens, 0);
|
||||||
|
match fun_decl {
|
||||||
|
Some(SyntaxResult::Err(err)) => {
|
||||||
|
assert_eq!(
|
||||||
|
err.reason,
|
||||||
|
"There should be an opening paren after the identifier, but found nothing"
|
||||||
|
);
|
||||||
|
assert_eq!(err.error_start, 4);
|
||||||
|
assert_eq!(err.error_end, 6);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_not_parse_fun_without_closing_paren() {
|
||||||
|
let tokens = get_tokens(&String::from("fun id(=")).unwrap();
|
||||||
|
let fun_decl = try_parse(&tokens, 0);
|
||||||
|
|
||||||
|
match fun_decl {
|
||||||
|
Some(SyntaxResult::Err(err)) => {
|
||||||
|
assert_eq!(
|
||||||
|
err.reason,
|
||||||
|
"There should be a closing paren after the parameter list, but found `=`"
|
||||||
|
);
|
||||||
|
assert_eq!(err.error_start, 7);
|
||||||
|
assert_eq!(err.error_end, 8);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||||
|
}
|
||||||
|
|
||||||
|
let tokens = get_tokens(&String::from("fun id(")).unwrap();
|
||||||
|
let fun_decl = try_parse(&tokens, 0);
|
||||||
|
match fun_decl {
|
||||||
|
Some(SyntaxResult::Err(err)) => {
|
||||||
|
assert_eq!(
|
||||||
|
err.reason,
|
||||||
|
"There should be a closing paren after the parameter list, but found nothing"
|
||||||
|
);
|
||||||
|
assert_eq!(err.error_start, 6);
|
||||||
|
assert_eq!(err.error_end, 7);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_not_parse_fun_without_opening_brace() {
|
||||||
|
let tokens = get_tokens(&String::from("fun id() =")).unwrap();
|
||||||
|
let fun_decl = try_parse(&tokens, 0);
|
||||||
|
|
||||||
|
match fun_decl {
|
||||||
|
Some(SyntaxResult::Err(err)) => {
|
||||||
|
assert_eq!(
|
||||||
|
err.reason,
|
||||||
|
"There should be an opening brace after the parameter list, but found `=`"
|
||||||
|
);
|
||||||
|
assert_eq!(err.error_start, 9);
|
||||||
|
assert_eq!(err.error_end, 10);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||||
|
}
|
||||||
|
|
||||||
|
let tokens = get_tokens(&String::from("fun id()")).unwrap();
|
||||||
|
let fun_decl = try_parse(&tokens, 0);
|
||||||
|
match fun_decl {
|
||||||
|
Some(SyntaxResult::Err(err)) => {
|
||||||
|
assert_eq!(
|
||||||
|
err.reason,
|
||||||
|
"There should be an opening brace after the parameter list, but found nothing"
|
||||||
|
);
|
||||||
|
assert_eq!(err.error_start, 7);
|
||||||
|
assert_eq!(err.error_end, 8);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_not_parse_fun_without_closing_brace() {
|
||||||
|
let tokens = get_tokens(&String::from("fun id() { 20")).unwrap();
|
||||||
|
let fun_decl = try_parse(&tokens, 0);
|
||||||
|
|
||||||
|
match fun_decl {
|
||||||
|
Some(SyntaxResult::Err(err)) => {
|
||||||
|
assert_eq!(
|
||||||
|
err.reason,
|
||||||
|
"There should be a closing brace after the function body, but found `20`"
|
||||||
|
);
|
||||||
|
assert_eq!(err.error_start, 11);
|
||||||
|
assert_eq!(err.error_end, 13);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||||
|
}
|
||||||
|
|
||||||
|
let tokens = get_tokens(&String::from("fun id() {")).unwrap();
|
||||||
|
let fun_decl = try_parse(&tokens, 0);
|
||||||
|
|
||||||
|
match fun_decl {
|
||||||
|
Some(SyntaxResult::Err(err)) => {
|
||||||
|
assert_eq!(
|
||||||
|
err.reason,
|
||||||
|
"There should be a closing brace after the function body, but found nothing"
|
||||||
|
);
|
||||||
|
assert_eq!(err.error_start, 9);
|
||||||
|
assert_eq!(err.error_end, 10);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected an error: {:?}", fun_decl),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_simple_function_declaration() {
|
||||||
|
let tokens = get_tokens(&String::from("fun id() {}")).unwrap();
|
||||||
|
let function_declaration = try_parse(&tokens, 0).unwrap();
|
||||||
|
|
||||||
|
match function_declaration {
|
||||||
|
SyntaxResult::Ok(TopLevelConstruct::FunctionDeclaration(declaration)) => {
|
||||||
|
assert_eq!(declaration.identifier, Box::new(String::from("id")));
|
||||||
|
}
|
||||||
|
_ => panic!(
|
||||||
|
"Expected a function declaration: {:?}",
|
||||||
|
function_declaration
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,16 +2,21 @@ use crate::error_handling::{MistiError, SyntaxError};
|
|||||||
|
|
||||||
mod binding;
|
mod binding;
|
||||||
mod expression;
|
mod expression;
|
||||||
|
mod function_declaration;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
|
|
||||||
use ast::{Binding, ModuleAST};
|
|
||||||
use crate::lexic::token::Token;
|
use crate::lexic::token::Token;
|
||||||
|
use ast::{Binding, ModuleAST};
|
||||||
|
|
||||||
|
use self::ast::TopLevelConstruct;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum SyntaxResult {
|
pub enum SyntaxResult {
|
||||||
///
|
///
|
||||||
/// A construct has been found
|
/// A construct has been found
|
||||||
Ok(Binding),
|
Ok(TopLevelConstruct),
|
||||||
///
|
///
|
||||||
/// No construct was found
|
/// No construct was found
|
||||||
None,
|
None,
|
||||||
|
15
src/syntax/utils.rs
Normal file
15
src/syntax/utils.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
use crate::{
|
||||||
|
lexic::token::{Token, TokenType},
|
||||||
|
utils::Result3,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn try_token_type(tokens: &Vec<Token>, pos: usize, token_type: TokenType) -> Result3<&Token> {
|
||||||
|
match tokens.get(pos) {
|
||||||
|
Some(t) if t.token_type == token_type => Result3::Ok(t),
|
||||||
|
Some(t) if t.token_type == TokenType::Semicolon || t.token_type == TokenType::EOF => {
|
||||||
|
Result3::None
|
||||||
|
}
|
||||||
|
Some(t) => Result3::Err(t),
|
||||||
|
None => Result3::None,
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user