Compare commits
10 Commits
1fbc353ebf
...
10eaa66a6d
Author | SHA1 | Date | |
---|---|---|---|
|
10eaa66a6d | ||
|
ed0e163283 | ||
|
f769a2ec1d | ||
|
18cbe2a8ab | ||
|
0630287e34 | ||
|
9af450eaa0 | ||
|
971b9d9516 | ||
|
c4d13e76bc | ||
|
03b5a1b6de | ||
|
2e9776de01 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
target
|
target
|
||||||
|
examples
|
||||||
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"rust-analyzer.inlayHints.typeHints.enable": false
|
||||||
|
}
|
13
CHANGELOG.md
13
CHANGELOG.md
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
|
- Parse multiple statements
|
||||||
|
- Parse binary operators
|
||||||
- Parse more complex bindings
|
- Parse more complex bindings
|
||||||
- Parse block of code
|
- Parse block of code
|
||||||
- Watch mode
|
- Watch mode
|
||||||
@ -14,6 +16,17 @@
|
|||||||
- Document code
|
- Document code
|
||||||
|
|
||||||
|
|
||||||
|
## v0.0.8
|
||||||
|
- Parse multiple statements inside a block
|
||||||
|
|
||||||
|
|
||||||
|
## v0.0.7
|
||||||
|
|
||||||
|
- Parse minimal function declarations following a grammar
|
||||||
|
- Parse function call, binding as statement
|
||||||
|
- Parse a statement as body of a function declaration
|
||||||
|
|
||||||
|
|
||||||
## v0.0.6
|
## v0.0.6
|
||||||
|
|
||||||
- Parse function declarations
|
- Parse function declarations
|
||||||
|
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -254,7 +254,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thp"
|
name = "thp"
|
||||||
version = "0.0.6"
|
version = "0.0.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"colored",
|
"colored",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "thp"
|
name = "thp"
|
||||||
version = "0.0.6"
|
version = "0.0.7"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::Transpilable;
|
use super::Transpilable;
|
||||||
use crate::syntax::ast::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.
|
||||||
@ -22,7 +22,10 @@ impl Transpilable for Binding {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::syntax::ast::{Binding, Expression, ValBinding};
|
use crate::syntax::ast::{
|
||||||
|
var_binding::{Binding, ValBinding},
|
||||||
|
Expression,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn binding_should_transpile() {
|
fn binding_should_transpile() {
|
||||||
|
@ -18,7 +18,10 @@ impl Transpilable for ModuleAST {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::syntax::ast::{Binding, Expression, TopLevelDeclaration, ValBinding};
|
use crate::syntax::ast::{
|
||||||
|
var_binding::{Binding, ValBinding},
|
||||||
|
Expression, TopLevelDeclaration,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn module_ast_should_transpile() {
|
fn module_ast_should_transpile() {
|
||||||
|
@ -32,12 +32,11 @@ enum Commands {
|
|||||||
R {},
|
R {},
|
||||||
}
|
}
|
||||||
|
|
||||||
const VERSION: &str = "0.0.6";
|
|
||||||
|
|
||||||
fn get_copyright() -> String {
|
fn get_copyright() -> String {
|
||||||
|
let crate_version = env!("CARGO_PKG_VERSION");
|
||||||
format!(
|
format!(
|
||||||
"THP {}\nCopyright (c) 2023 Fernando Enrique Araoz Morales\n",
|
"THP {}\nCopyright (c) 2023 Fernando Enrique Araoz Morales\n",
|
||||||
VERSION,
|
crate_version,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
12
src/syntax/ast/functions.rs
Normal file
12
src/syntax/ast/functions.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FunctionCall {
|
||||||
|
pub identifier: Box<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ArgumentsList {
|
||||||
|
pub arguments: Vec<Box<Argument>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Argument {}
|
@ -1,10 +1,14 @@
|
|||||||
|
pub mod functions;
|
||||||
|
pub mod statement;
|
||||||
|
pub mod var_binding;
|
||||||
|
|
||||||
pub struct ModuleAST {
|
pub struct ModuleAST {
|
||||||
pub declarations: Vec<TopLevelDeclaration>,
|
pub declarations: Vec<TopLevelDeclaration>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum TopLevelDeclaration {
|
pub enum TopLevelDeclaration {
|
||||||
Binding(Binding),
|
Binding(var_binding::Binding),
|
||||||
FunctionDeclaration(FunctionDeclaration),
|
FunctionDeclaration(FunctionDeclaration),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15,31 +19,13 @@ pub struct FunctionDeclaration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Block {}
|
pub struct Block {
|
||||||
|
pub statements: Vec<statement::Statement>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ParamsList {}
|
pub struct ParamsList {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Binding {
|
|
||||||
Val(ValBinding),
|
|
||||||
Var(VarBinding),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ValBinding {
|
|
||||||
pub datatype: Option<String>,
|
|
||||||
pub identifier: Box<String>,
|
|
||||||
pub expression: Expression,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct VarBinding {
|
|
||||||
pub datatype: Option<String>,
|
|
||||||
pub identifier: Box<String>,
|
|
||||||
pub expression: Expression,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Expression {
|
pub enum Expression {
|
||||||
Number(Box<String>),
|
Number(Box<String>),
|
7
src/syntax/ast/statement.rs
Normal file
7
src/syntax/ast/statement.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
use super::{functions::FunctionCall, var_binding::Binding};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Statement {
|
||||||
|
FunctionCall(FunctionCall),
|
||||||
|
Binding(Binding),
|
||||||
|
}
|
21
src/syntax/ast/var_binding.rs
Normal file
21
src/syntax/ast/var_binding.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use super::Expression;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Binding {
|
||||||
|
Val(ValBinding),
|
||||||
|
Var(VarBinding),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ValBinding {
|
||||||
|
pub datatype: Option<String>,
|
||||||
|
pub identifier: Box<String>,
|
||||||
|
pub expression: Expression,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct VarBinding {
|
||||||
|
pub datatype: Option<String>,
|
||||||
|
pub identifier: Box<String>,
|
||||||
|
pub expression: Expression,
|
||||||
|
}
|
@ -1,147 +1,118 @@
|
|||||||
use super::ast::{Binding, ValBinding, VarBinding};
|
use super::ast::var_binding::{Binding, ValBinding, VarBinding};
|
||||||
use super::utils::{try_operator, try_token_type};
|
use super::utils::{try_operator, try_token_type, parse_token_type};
|
||||||
use super::{expression, SyntaxResult};
|
use super::{expression, ParseResult};
|
||||||
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;
|
use crate::utils::Result3;
|
||||||
|
|
||||||
// TODO: Should return a 3 state value:
|
pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Binding, ()> {
|
||||||
// - Success: binding parsed successfully
|
|
||||||
// - NotFound: the first token (var | val) was not found, so the parser should try other options
|
|
||||||
// - Error: token (var | val) was found, but then other expected tokens were not found
|
|
||||||
pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> Option<SyntaxResult> {
|
|
||||||
let mut current_pos = pos;
|
let mut current_pos = pos;
|
||||||
// Optional datatype annotation
|
|
||||||
let datatype_annotation = {
|
|
||||||
match try_token_type(tokens, current_pos, TokenType::Datatype) {
|
|
||||||
Result3::Ok(t) => {
|
|
||||||
current_pos += 1;
|
|
||||||
Some(String::from(&t.value))
|
|
||||||
}
|
|
||||||
Result3::Err(_) => None,
|
|
||||||
Result3::None => panic!(
|
|
||||||
"Internal compiler error: Illegal token stream at src/syntax/binding.rs#try_parse"
|
|
||||||
),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* val/var keyword
|
* val/var keyword
|
||||||
*/
|
*/
|
||||||
let (is_val, binding_token) = {
|
let (is_val, binding_token, next_pos) = {
|
||||||
let res1 = try_token_type(tokens, current_pos, TokenType::VAL);
|
let res1 = parse_token_type(tokens, current_pos, TokenType::VAL);
|
||||||
match res1 {
|
match res1 {
|
||||||
Result3::Ok(val_token) => (true, val_token),
|
ParseResult::Ok(val_token, next) => (true, val_token, next),
|
||||||
_ => {
|
_ => {
|
||||||
let res2 = try_token_type(tokens, current_pos, TokenType::VAR);
|
let res2 = parse_token_type(tokens, current_pos, TokenType::VAR);
|
||||||
match res2 {
|
match res2 {
|
||||||
Result3::Ok(var_token) => (false, var_token),
|
ParseResult::Ok(var_token, next) => (false, var_token, next),
|
||||||
// Neither VAL nor VAR were matched, the parser should try
|
// Neither VAL nor VAR were matched, the caller should try
|
||||||
// other constructs
|
// other constructs
|
||||||
_ => return None,
|
_ => return ParseResult::Unmatched,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
current_pos = next_pos;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* identifier
|
* identifier
|
||||||
*/
|
*/
|
||||||
let identifier = match try_token_type(tokens, current_pos + 1, TokenType::Identifier) {
|
let (identifier, next_pos) = match parse_token_type(tokens, current_pos, TokenType::Identifier) {
|
||||||
Result3::Ok(t) => t,
|
ParseResult::Ok(t, n) => (t, n),
|
||||||
Result3::Err(t) => {
|
ParseResult::Err(error) => {
|
||||||
// The parser found a token, but it's not an identifier
|
// The parser found a token, but it's not an identifier
|
||||||
return Some(SyntaxResult::Err(SyntaxError {
|
return ParseResult::Err(error);
|
||||||
reason: format!(
|
|
||||||
"There should be an identifier after a `{}` token",
|
|
||||||
if is_val { "val" } else { "var" }
|
|
||||||
),
|
|
||||||
error_start: t.position,
|
|
||||||
error_end: t.get_end_position(),
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
Result3::None => {
|
_ => {
|
||||||
// The parser didn't find an Identifier after VAL/VAR
|
// The parser didn't find an Identifier after VAL/VAR
|
||||||
return Some(SyntaxResult::Err(SyntaxError {
|
return ParseResult::Err(SyntaxError {
|
||||||
reason: format!(
|
reason: format!(
|
||||||
"There should be an identifier after a `{}` token",
|
"There should be an identifier after a `{}` token",
|
||||||
if is_val { "val" } else { "var" }
|
if is_val { "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;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Equal (=) operator
|
* Equal (=) operator
|
||||||
*/
|
*/
|
||||||
let equal_operator: &Token = match try_operator(tokens, current_pos + 2, String::from("=")) {
|
let equal_operator: &Token = match try_operator(tokens, current_pos, String::from("=")) {
|
||||||
Result3::Ok(t) => t,
|
Result3::Ok(t) => t,
|
||||||
Result3::Err(t) => {
|
Result3::Err(t) => {
|
||||||
// The parser found a token, but it's not the `=` operator
|
// The parser found a token, but it's not the `=` operator
|
||||||
return Some(SyntaxResult::Err(SyntaxError {
|
return ParseResult::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 => {
|
Result3::None => {
|
||||||
// The parser didn't find the `=` operator after the identifier
|
// The parser didn't find the `=` operator after the identifier
|
||||||
return Some(SyntaxResult::Err(SyntaxError {
|
return ParseResult::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(),
|
||||||
}));
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let expression = expression::try_parse(tokens, current_pos + 3);
|
let expression = expression::try_parse(tokens, current_pos + 1);
|
||||||
if expression.is_none() {
|
if expression.is_none() {
|
||||||
return Some(SyntaxResult::Err(SyntaxError {
|
return ParseResult::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(),
|
||||||
}));
|
});
|
||||||
}
|
}
|
||||||
let expression = expression.unwrap();
|
let expression = expression.unwrap();
|
||||||
|
|
||||||
let binding = if is_val {
|
let binding = if is_val {
|
||||||
Binding::Val(ValBinding {
|
Binding::Val(ValBinding {
|
||||||
datatype: datatype_annotation,
|
datatype: None,
|
||||||
identifier: Box::new(identifier.value.clone()),
|
identifier: Box::new(identifier.value.clone()),
|
||||||
expression,
|
expression,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Binding::Var(VarBinding {
|
Binding::Var(VarBinding {
|
||||||
datatype: datatype_annotation,
|
datatype: None,
|
||||||
identifier: Box::new(identifier.value.clone()),
|
identifier: Box::new(identifier.value.clone()),
|
||||||
expression,
|
expression,
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(SyntaxResult::Ok(
|
ParseResult::Ok(binding, current_pos + 2)
|
||||||
super::ast::TopLevelDeclaration::Binding(binding),
|
|
||||||
current_pos + 4,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{lexic::get_tokens, syntax::ast::TopLevelDeclaration};
|
use crate::lexic::get_tokens;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_parse_val_binding() {
|
fn should_parse_val_binding() {
|
||||||
let tokens = get_tokens(&String::from("val identifier = 20")).unwrap();
|
let tokens = get_tokens(&String::from("val identifier = 20")).unwrap();
|
||||||
let binding = try_parse(&tokens, 0).unwrap();
|
let ParseResult::Ok(Binding::Val(binding), _) = try_parse(&tokens, 0) else {
|
||||||
|
panic!()
|
||||||
|
};
|
||||||
|
|
||||||
match binding {
|
assert_eq!("identifier", format!("{}", binding.identifier));
|
||||||
SyntaxResult::Ok(TopLevelDeclaration::Binding(Binding::Val(binding)), _) => {
|
|
||||||
assert_eq!("identifier", format!("{}", binding.identifier));
|
|
||||||
}
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -169,40 +140,36 @@ mod tests {
|
|||||||
assert_eq!("=", token.value);
|
assert_eq!("=", token.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
#[test]
|
#[test]
|
||||||
fn should_parse_binding_with_datatype() {
|
fn should_parse_binding_with_datatype() {
|
||||||
let tokens = get_tokens(&String::from("Num val identifier = 20")).unwrap();
|
let tokens = get_tokens(&String::from("Num val identifier = 20")).unwrap();
|
||||||
let binding = try_parse(&tokens, 0).unwrap();
|
let ParseResult::Ok(Binding::Val(binding), _) = try_parse(&tokens, 0) else {
|
||||||
|
panic!()
|
||||||
|
};
|
||||||
|
|
||||||
match binding {
|
assert_eq!(Some(String::from("Num")), binding.datatype);
|
||||||
SyntaxResult::Ok(TopLevelDeclaration::Binding(Binding::Val(binding)), _) => {
|
assert_eq!("identifier", format!("{}", binding.identifier));
|
||||||
assert_eq!(Some(String::from("Num")), binding.datatype);
|
|
||||||
assert_eq!("identifier", format!("{}", binding.identifier));
|
|
||||||
}
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
|
|
||||||
let tokens = get_tokens(&String::from("Bool var identifier = true")).unwrap();
|
let tokens = get_tokens(&String::from("Bool var identifier = 20")).unwrap();
|
||||||
let binding = try_parse(&tokens, 0).unwrap();
|
let ParseResult::Ok(Binding::Var(binding), _) = try_parse(&tokens, 0) else {
|
||||||
|
panic!()
|
||||||
|
};
|
||||||
|
|
||||||
match binding {
|
assert_eq!(Some(String::from("Bool")), binding.datatype);
|
||||||
SyntaxResult::Ok(TopLevelDeclaration::Binding(Binding::Var(binding)), _) => {
|
assert_eq!("identifier", format!("{}", binding.identifier));
|
||||||
assert_eq!(Some(String::from("Bool")), binding.datatype);
|
|
||||||
assert_eq!("identifier", format!("{}", binding.identifier));
|
|
||||||
}
|
|
||||||
_ => panic!("D: {:?}", binding),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_return_correct_error() {
|
fn should_return_correct_error() {
|
||||||
let tokens = get_tokens(&String::from("val")).unwrap();
|
let tokens = get_tokens(&String::from("val")).unwrap();
|
||||||
assert_eq!(TokenType::VAL, tokens[0].token_type);
|
assert_eq!(TokenType::VAL, tokens[0].token_type);
|
||||||
assert_eq!(0, tokens[0].position);
|
assert_eq!(0, tokens[0].position);
|
||||||
let binding = try_parse(&tokens, 0).unwrap();
|
let binding = try_parse(&tokens, 0);
|
||||||
|
|
||||||
match binding {
|
match binding {
|
||||||
SyntaxResult::Err(error) => {
|
ParseResult::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);
|
||||||
}
|
}
|
||||||
@ -215,10 +182,10 @@ mod tests {
|
|||||||
let tokens = get_tokens(&String::from("val 322")).unwrap();
|
let tokens = get_tokens(&String::from("val 322")).unwrap();
|
||||||
assert_eq!(TokenType::VAL, tokens[0].token_type);
|
assert_eq!(TokenType::VAL, tokens[0].token_type);
|
||||||
assert_eq!(0, tokens[0].position);
|
assert_eq!(0, tokens[0].position);
|
||||||
let binding = try_parse(&tokens, 0).unwrap();
|
let binding = try_parse(&tokens, 0);
|
||||||
|
|
||||||
match binding {
|
match binding {
|
||||||
SyntaxResult::Err(error) => {
|
ParseResult::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);
|
||||||
}
|
}
|
||||||
@ -226,10 +193,10 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let tokens = get_tokens(&String::from("val \"hello\"")).unwrap();
|
let tokens = get_tokens(&String::from("val \"hello\"")).unwrap();
|
||||||
let binding = try_parse(&tokens, 0).unwrap();
|
let binding = try_parse(&tokens, 0);
|
||||||
|
|
||||||
match binding {
|
match binding {
|
||||||
SyntaxResult::Err(error) => {
|
ParseResult::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);
|
||||||
}
|
}
|
||||||
@ -240,10 +207,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_return_error_when_equal_op_is_wrong() {
|
fn should_return_error_when_equal_op_is_wrong() {
|
||||||
let tokens = get_tokens(&String::from("val id \"error\"")).unwrap();
|
let tokens = get_tokens(&String::from("val id \"error\"")).unwrap();
|
||||||
let binding = try_parse(&tokens, 0).unwrap();
|
let binding = try_parse(&tokens, 0);
|
||||||
|
|
||||||
match binding {
|
match binding {
|
||||||
SyntaxResult::Err(error) => {
|
ParseResult::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);
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,37 @@ pub fn parse_block<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Block,
|
|||||||
};
|
};
|
||||||
current_pos = next_pos;
|
current_pos = next_pos;
|
||||||
|
|
||||||
|
// Parse block statements
|
||||||
|
let mut statements = Vec::new();
|
||||||
|
|
||||||
|
// First statement
|
||||||
|
match super::statement::try_parse(tokens, current_pos) {
|
||||||
|
ParseResult::Ok(statement, next_pos) => {
|
||||||
|
current_pos = next_pos;
|
||||||
|
statements.push(statement);
|
||||||
|
}
|
||||||
|
ParseResult::Err(err) => return ParseResult::Err(err),
|
||||||
|
ParseResult::Unmatched => {}
|
||||||
|
ParseResult::Mismatch(_) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// More statements separated by new lines
|
||||||
|
while let Some(t) = tokens.get(current_pos) {
|
||||||
|
if t.token_type != TokenType::NewLine {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
current_pos += 1;
|
||||||
|
|
||||||
|
match super::statement::try_parse(tokens, current_pos) {
|
||||||
|
ParseResult::Ok(statement, next_pos) => {
|
||||||
|
current_pos = next_pos;
|
||||||
|
statements.push(statement);
|
||||||
|
}
|
||||||
|
ParseResult::Err(err) => return ParseResult::Err(err),
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
@ -40,5 +71,37 @@ pub fn parse_block<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Block,
|
|||||||
};
|
};
|
||||||
current_pos = next_pos;
|
current_pos = next_pos;
|
||||||
|
|
||||||
ParseResult::Ok(Block {}, current_pos)
|
ParseResult::Ok(Block { statements }, current_pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::lexic::get_tokens;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_block() {
|
||||||
|
let tokens = get_tokens(&String::from("{f()}")).unwrap();
|
||||||
|
let block = parse_block(&tokens, 0);
|
||||||
|
|
||||||
|
let block = match block {
|
||||||
|
ParseResult::Ok(p, _) => p,
|
||||||
|
_ => panic!("Expected a block, got: {:?}", block),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(block.statements.len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_block_2() {
|
||||||
|
let tokens = get_tokens(&String::from("{f()\ng()}")).unwrap();
|
||||||
|
let block = parse_block(&tokens, 0);
|
||||||
|
|
||||||
|
let block = match block {
|
||||||
|
ParseResult::Ok(p, _) => p,
|
||||||
|
_ => panic!("Expected a block, got: {:?}", block),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(block.statements.len(), 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
92
src/syntax/functions/arguments_list.rs
Normal file
92
src/syntax/functions/arguments_list.rs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
use crate::{
|
||||||
|
error_handling::SyntaxError,
|
||||||
|
lexic::token::{Token, TokenType},
|
||||||
|
syntax::{ast::functions::ArgumentsList, utils::parse_token_type, ParseResult},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<ArgumentsList, &Token> {
|
||||||
|
let mut current_pos = pos;
|
||||||
|
|
||||||
|
let (opening_paren, next_pos) =
|
||||||
|
match parse_token_type(tokens, current_pos, TokenType::LeftParen) {
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Parse closing paren
|
||||||
|
let (_closing_paren, next_pos) =
|
||||||
|
match parse_token_type(tokens, current_pos, TokenType::RightParen) {
|
||||||
|
ParseResult::Ok(t, next) => (t, next),
|
||||||
|
ParseResult::Err(err) => return ParseResult::Err(err),
|
||||||
|
ParseResult::Mismatch(t) => {
|
||||||
|
return ParseResult::Err(SyntaxError {
|
||||||
|
reason: String::from("Expected a closing paren after the function identifier."),
|
||||||
|
error_start: t.position,
|
||||||
|
error_end: t.get_end_position(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ParseResult::Unmatched => {
|
||||||
|
return ParseResult::Err(SyntaxError {
|
||||||
|
reason: String::from("Expected a closing paren after the function identifier."),
|
||||||
|
error_start: opening_paren.position,
|
||||||
|
error_end: opening_paren.get_end_position(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
current_pos = next_pos;
|
||||||
|
|
||||||
|
ParseResult::Ok(
|
||||||
|
ArgumentsList {
|
||||||
|
arguments: Vec::new(),
|
||||||
|
},
|
||||||
|
current_pos,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::lexic::get_tokens;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_empty_list() {
|
||||||
|
let tokens = get_tokens(&String::from("()")).unwrap();
|
||||||
|
let fun_decl = try_parse(&tokens, 0);
|
||||||
|
|
||||||
|
let ParseResult::Ok(list, next) = fun_decl else {
|
||||||
|
panic!("Expected an unmatched result: {:?}", fun_decl);
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(next, 2);
|
||||||
|
assert_eq!(list.arguments.len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_empty_list_with_whitespace() {
|
||||||
|
let tokens = get_tokens(&String::from("( ) ")).unwrap();
|
||||||
|
let fun_decl = try_parse(&tokens, 0);
|
||||||
|
|
||||||
|
let ParseResult::Ok(list, next) = fun_decl else {
|
||||||
|
panic!("Expected a result, got: {:?}", fun_decl);
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(next, 2);
|
||||||
|
assert_eq!(list.arguments.len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_empty_list_with_whitespace_2() {
|
||||||
|
let tokens = get_tokens(&String::from("(\n \n)")).unwrap();
|
||||||
|
let fun_decl = try_parse(&tokens, 0);
|
||||||
|
|
||||||
|
let ParseResult::Ok(list, next) = fun_decl else {
|
||||||
|
panic!("Expected a result, got: {:?}", fun_decl);
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(next, 3);
|
||||||
|
assert_eq!(list.arguments.len(), 0);
|
||||||
|
}
|
||||||
|
}
|
96
src/syntax/functions/function_call.rs
Normal file
96
src/syntax/functions/function_call.rs
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
use crate::{
|
||||||
|
error_handling::SyntaxError,
|
||||||
|
lexic::token::{Token, TokenType},
|
||||||
|
syntax::{ast::functions::FunctionCall, utils::parse_token_type, ParseResult},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<FunctionCall, ()> {
|
||||||
|
let mut current_pos = pos;
|
||||||
|
|
||||||
|
// TODO: Use an expression instead of a fixed identifier
|
||||||
|
// Parse identifier
|
||||||
|
let (identifier, next_pos) = match parse_token_type(tokens, current_pos, TokenType::Identifier)
|
||||||
|
{
|
||||||
|
ParseResult::Ok(id, next) => (id, next),
|
||||||
|
ParseResult::Err(err) => return ParseResult::Err(err),
|
||||||
|
ParseResult::Mismatch(_) => {
|
||||||
|
return ParseResult::Unmatched;
|
||||||
|
}
|
||||||
|
ParseResult::Unmatched => {
|
||||||
|
return ParseResult::Unmatched;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
current_pos = next_pos;
|
||||||
|
|
||||||
|
// Parse arguments list
|
||||||
|
let (args_list, next_pos) = match super::arguments_list::try_parse(tokens, current_pos) {
|
||||||
|
ParseResult::Ok(args, next) => (args, next),
|
||||||
|
ParseResult::Err(err) => return ParseResult::Err(err),
|
||||||
|
ParseResult::Mismatch(_) => {
|
||||||
|
return ParseResult::Unmatched;
|
||||||
|
}
|
||||||
|
ParseResult::Unmatched => {
|
||||||
|
return ParseResult::Unmatched;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
current_pos = next_pos;
|
||||||
|
|
||||||
|
ParseResult::Ok(
|
||||||
|
FunctionCall {
|
||||||
|
identifier: Box::new(identifier.value.clone()),
|
||||||
|
},
|
||||||
|
current_pos,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::lexic::get_tokens;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_not_parse_identifier_alone() {
|
||||||
|
let tokens = get_tokens(&String::from("function_name")).unwrap();
|
||||||
|
let fun_decl = try_parse(&tokens, 0);
|
||||||
|
|
||||||
|
let ParseResult::Unmatched = fun_decl else {
|
||||||
|
panic!("Expected an unmatched result: {:?}", fun_decl);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_minimal_construct() {
|
||||||
|
let tokens = get_tokens(&String::from("function_name()")).unwrap();
|
||||||
|
let fun_decl = try_parse(&tokens, 0);
|
||||||
|
|
||||||
|
let ParseResult::Ok(_call, next) = fun_decl else {
|
||||||
|
panic!("Expected a result, got: {:?}", fun_decl);
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(next, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_minimal_construct_2() {
|
||||||
|
let tokens = get_tokens(&String::from("function_name ( )")).unwrap();
|
||||||
|
let fun_decl = try_parse(&tokens, 0);
|
||||||
|
|
||||||
|
let ParseResult::Ok(_call, next) = fun_decl else {
|
||||||
|
panic!("Expected a result, got: {:?}", fun_decl);
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(next, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_minimal_construct_3() {
|
||||||
|
let tokens = get_tokens(&String::from("function_name\n(\n \n)")).unwrap();
|
||||||
|
let fun_decl = try_parse(&tokens, 0);
|
||||||
|
|
||||||
|
let ParseResult::Ok(_call, next) = fun_decl else {
|
||||||
|
panic!("Expected a result, got: {:?}", fun_decl);
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(next, 5);
|
||||||
|
}
|
||||||
|
}
|
@ -4,8 +4,8 @@ use crate::{
|
|||||||
utils::Result3,
|
utils::Result3,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::super::{
|
||||||
ast::{FunctionDeclaration, ParamsList},
|
ast::FunctionDeclaration,
|
||||||
block::parse_block,
|
block::parse_block,
|
||||||
params_list::parse_params_list,
|
params_list::parse_params_list,
|
||||||
utils::{parse_token_type, try_token_type},
|
utils::{parse_token_type, try_token_type},
|
||||||
@ -98,7 +98,7 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Function
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{lexic::get_tokens, syntax::ast::TopLevelDeclaration};
|
use crate::lexic::get_tokens;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -317,7 +317,7 @@ mod tests {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod whitespace_test {
|
mod whitespace_test {
|
||||||
use crate::{lexic::get_tokens, syntax::ast::TopLevelDeclaration};
|
use crate::lexic::get_tokens;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
3
src/syntax/functions/mod.rs
Normal file
3
src/syntax/functions/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub mod arguments_list;
|
||||||
|
pub mod function_call;
|
||||||
|
pub mod function_declaration;
|
@ -38,8 +38,28 @@ return type = ;
|
|||||||
### Block
|
### Block
|
||||||
|
|
||||||
```ebnf
|
```ebnf
|
||||||
block = "{", "}"
|
block = "{", (statement, (new line, statement)*)?, "}"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Statement
|
||||||
|
|
||||||
|
```ebnf
|
||||||
|
statement = function call | binding
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Function call
|
||||||
|
|
||||||
|
```ebnf
|
||||||
|
function call = identifier, arguments list
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Arguments list
|
||||||
|
|
||||||
|
```ebnf
|
||||||
|
arguments list = "(", ")"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,8 +3,9 @@ use crate::error_handling::{MistiError, SyntaxError};
|
|||||||
mod binding;
|
mod binding;
|
||||||
mod block;
|
mod block;
|
||||||
mod expression;
|
mod expression;
|
||||||
mod function_declaration;
|
mod functions;
|
||||||
mod params_list;
|
mod params_list;
|
||||||
|
mod statement;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
@ -14,24 +15,18 @@ use ast::ModuleAST;
|
|||||||
|
|
||||||
use self::ast::TopLevelDeclaration;
|
use self::ast::TopLevelDeclaration;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum SyntaxResult {
|
|
||||||
///
|
|
||||||
/// A construct has been found
|
|
||||||
Ok(TopLevelDeclaration, usize),
|
|
||||||
///
|
|
||||||
/// No construct was found
|
|
||||||
None,
|
|
||||||
///
|
|
||||||
/// A construct was found, but there was an error parsing it
|
|
||||||
Err(SyntaxError),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ParseResult<A, B> {
|
pub enum ParseResult<A, B> {
|
||||||
|
/// The parsing was a success
|
||||||
Ok(A, usize),
|
Ok(A, usize),
|
||||||
|
/// The parsing failed past a point of no return.
|
||||||
|
///
|
||||||
|
/// For example, when parsing a function declaration
|
||||||
|
/// the `fun` token is found, but then no identifier
|
||||||
Err(SyntaxError),
|
Err(SyntaxError),
|
||||||
|
/// A construct different from the one expected was found
|
||||||
Mismatch(B),
|
Mismatch(B),
|
||||||
|
/// No construct was found
|
||||||
Unmatched,
|
Unmatched,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +71,7 @@ fn next_construct<'a>(
|
|||||||
current_pos: usize,
|
current_pos: usize,
|
||||||
) -> ParseResult<TopLevelDeclaration, ()> {
|
) -> ParseResult<TopLevelDeclaration, ()> {
|
||||||
None.or_else(
|
None.or_else(
|
||||||
|| match function_declaration::try_parse(tokens, current_pos) {
|
|| match functions::function_declaration::try_parse(tokens, current_pos) {
|
||||||
ParseResult::Ok(declaration, next_pos) => Some(ParseResult::Ok(
|
ParseResult::Ok(declaration, next_pos) => Some(ParseResult::Ok(
|
||||||
TopLevelDeclaration::FunctionDeclaration(declaration),
|
TopLevelDeclaration::FunctionDeclaration(declaration),
|
||||||
next_pos,
|
next_pos,
|
||||||
|
56
src/syntax/statement.rs
Normal file
56
src/syntax/statement.rs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
use crate::lexic::token::Token;
|
||||||
|
|
||||||
|
use super::{ast::statement::Statement, binding, functions::function_call, ParseResult};
|
||||||
|
|
||||||
|
pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Statement, ()> {
|
||||||
|
None.or_else(|| match binding::try_parse(tokens, pos) {
|
||||||
|
ParseResult::Ok(b, next) => Some(ParseResult::Ok(Statement::Binding(b), next)),
|
||||||
|
ParseResult::Err(err) => Some(ParseResult::Err(err)),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.or_else(|| match function_call::try_parse(tokens, pos) {
|
||||||
|
ParseResult::Ok(f, next) => Some(ParseResult::Ok(Statement::FunctionCall(f), next)),
|
||||||
|
ParseResult::Err(err) => Some(ParseResult::Err(err)),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| ParseResult::Unmatched)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_function_call() {
|
||||||
|
let input = String::from("f1()");
|
||||||
|
let tokens = crate::lexic::get_tokens(&input).unwrap();
|
||||||
|
let statement = try_parse(&tokens, 0);
|
||||||
|
|
||||||
|
let statement = match statement {
|
||||||
|
ParseResult::Ok(s, _) => s,
|
||||||
|
_ => panic!("Expected a statement"),
|
||||||
|
};
|
||||||
|
|
||||||
|
match statement {
|
||||||
|
Statement::FunctionCall(_) => assert!(true),
|
||||||
|
_ => panic!("Expected a function call"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_binding() {
|
||||||
|
let input = String::from("val identifier = 20");
|
||||||
|
let tokens = crate::lexic::get_tokens(&input).unwrap();
|
||||||
|
let statement = try_parse(&tokens, 0);
|
||||||
|
|
||||||
|
let statement = match statement {
|
||||||
|
ParseResult::Ok(s, _) => s,
|
||||||
|
_ => panic!("Expected a statement"),
|
||||||
|
};
|
||||||
|
|
||||||
|
match statement {
|
||||||
|
Statement::Binding(_) => assert!(true),
|
||||||
|
_ => panic!("Expected a binding"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,9 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
error_handling::SyntaxError,
|
|
||||||
lexic::token::{Token, TokenType},
|
lexic::token::{Token, TokenType},
|
||||||
utils::Result3,
|
utils::Result3,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{ParseResult, SyntaxResult};
|
use super::ParseResult;
|
||||||
|
|
||||||
/// Expects the token at `pos` to be of type `token_type`
|
/// Expects the token at `pos` to be of type `token_type`
|
||||||
pub fn try_token_type(tokens: &Vec<Token>, pos: usize, token_type: TokenType) -> Result3<&Token> {
|
pub fn try_token_type(tokens: &Vec<Token>, pos: usize, token_type: TokenType) -> Result3<&Token> {
|
||||||
@ -18,6 +17,18 @@ pub fn try_token_type(tokens: &Vec<Token>, pos: usize, token_type: TokenType) ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 => {
|
||||||
|
Result3::None
|
||||||
|
}
|
||||||
|
Some(t) => Result3::Err(t),
|
||||||
|
None => Result3::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Expects the token at `pos` to be of type `token_type`
|
/// Expects the token at `pos` to be of type `token_type`
|
||||||
pub fn parse_token_type(
|
pub fn parse_token_type(
|
||||||
tokens: &Vec<Token>,
|
tokens: &Vec<Token>,
|
||||||
@ -48,58 +59,3 @@ pub fn parse_token_type(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 => {
|
|
||||||
Result3::None
|
|
||||||
}
|
|
||||||
Some(t) => Result3::Err(t),
|
|
||||||
None => Result3::None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn _try_operator_w<'a>(
|
|
||||||
tokens: &'a Vec<Token>,
|
|
||||||
pos: usize,
|
|
||||||
operator: String,
|
|
||||||
error_message: String,
|
|
||||||
prev_token: &Token,
|
|
||||||
) -> Result<(&'a Token, usize), Option<SyntaxResult>> {
|
|
||||||
let mut current_pos = pos;
|
|
||||||
|
|
||||||
// Ignore all whitespace and newlines
|
|
||||||
while let Some(t) = tokens.get(current_pos) {
|
|
||||||
if t.token_type == TokenType::INDENT
|
|
||||||
|| t.token_type == TokenType::DEDENT
|
|
||||||
|| t.token_type == TokenType::NewLine
|
|
||||||
{
|
|
||||||
current_pos += 1;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match tokens.get(current_pos) {
|
|
||||||
Some(t) if t.token_type == TokenType::Operator && t.value == operator => {
|
|
||||||
Ok((t, current_pos + 1))
|
|
||||||
}
|
|
||||||
Some(t) if t.token_type == TokenType::NewLine || t.token_type == TokenType::EOF => {
|
|
||||||
Err(Some(SyntaxResult::Err(SyntaxError {
|
|
||||||
reason: error_message,
|
|
||||||
error_start: prev_token.position,
|
|
||||||
error_end: prev_token.get_end_position(),
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
Some(t) => Err(Some(SyntaxResult::Err(SyntaxError {
|
|
||||||
reason: error_message,
|
|
||||||
error_start: t.position,
|
|
||||||
error_end: t.get_end_position(),
|
|
||||||
}))),
|
|
||||||
None => Err(Some(SyntaxResult::Err(SyntaxError {
|
|
||||||
reason: error_message,
|
|
||||||
error_start: prev_token.position,
|
|
||||||
error_end: prev_token.get_end_position(),
|
|
||||||
}))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user