Skip to content

Commit d8314ea

Browse files
committed
(parser) fix: handle overflow errors on depth incrementation
1 parent a777a53 commit d8314ea

File tree

3 files changed

+138
-46
lines changed

3 files changed

+138
-46
lines changed

src/parser/modifiers/functions.rs

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,76 @@
11
//! Module that modifies [`FunctionCall`] within an existing node.
22
3+
use core::convert::Infallible;
34
use core::mem;
5+
use core::ops::{ControlFlow, FromResidual, Try};
46

57
use crate::parser::keyword::control_flow::traits::ControlFlow as _;
68
use crate::parser::operators::api::{Binary, Ternary, Unary};
79
use crate::parser::symbols::api::{BracedBlock, FunctionCall, ListInitialiser};
810
use crate::parser::tree::Ast;
911

12+
/// Result of call to `[MakeFunction::can_make_function]`, indicated whether a
13+
/// function can be created from a `(` and a pending variable, and if so, at
14+
/// what depth.
15+
pub enum CanMakeFnRes {
16+
/// A function can be made.
17+
///
18+
/// The depth of the variable that is to be turned into a function is
19+
/// stored. The depth is the number of variable to descend in the
20+
/// `[Ast]` before reaching the desired variable.
21+
CanMakeFn(u32),
22+
/// No function can be made
23+
///
24+
/// No pending variable was found.
25+
None,
26+
/// An error occured whilst incrementing the depth.
27+
///
28+
/// This means that the user has more than 2**32 nested variables.
29+
TooDeep,
30+
}
31+
32+
impl CanMakeFnRes {
33+
/// Tries to increment the variable depth if it exists.
34+
///
35+
/// If it doesn't exist it initialises the depth to 0.
36+
const fn increment_or_default(self) -> Self {
37+
match self {
38+
Self::CanMakeFn(depth) => match depth.checked_add(1) {
39+
Some(new_depth) => Self::CanMakeFn(new_depth),
40+
None => Self::TooDeep,
41+
},
42+
Self::None => Self::CanMakeFn(0),
43+
Self::TooDeep => Self::TooDeep,
44+
}
45+
}
46+
}
47+
48+
impl FromResidual<Self> for CanMakeFnRes {
49+
fn from_residual(residual: Self) -> Self {
50+
residual
51+
}
52+
}
53+
54+
impl FromResidual<Option<Infallible>> for CanMakeFnRes {
55+
fn from_residual(residual: Option<Infallible>) -> Self {
56+
residual.map_or(Self::None, |_| unreachable!())
57+
}
58+
}
59+
60+
impl Try for CanMakeFnRes {
61+
type Output = Self;
62+
63+
type Residual = Self;
64+
65+
fn branch(self) -> ControlFlow<Self, Self> {
66+
ControlFlow::Continue(self)
67+
}
68+
69+
fn from_output(output: Self) -> Self {
70+
output
71+
}
72+
}
73+
1074
/// Trait to manipulate node to find and edit [`Variable`]s that can be
1175
/// transformed into functions if a `(` is read.
1276
pub trait MakeFunction {
@@ -15,33 +79,31 @@ pub trait MakeFunction {
1579
/// # Returns
1680
///
1781
/// The depth of the variable in the AST that is to be made into a function.
18-
fn can_make_function(&self) -> Option<u32>;
82+
fn can_make_function(&self) -> CanMakeFnRes;
1983

2084
/// Makes a function out of the variable found in [`can_make_function`].
2185
fn make_function(&mut self, depth: u32, arguments: Vec<Ast>);
2286
}
2387

2488
impl MakeFunction for Ast {
25-
fn can_make_function(&self) -> Option<u32> {
89+
fn can_make_function(&self) -> CanMakeFnRes {
2690
match self {
27-
Self::Variable(variable) => Some(variable.can_make_function().unwrap_or_default()),
91+
Self::Variable(variable) => variable.can_make_function().increment_or_default(),
2892
Self::Empty
2993
| Self::Cast(_)
3094
| Self::Leaf(_)
3195
| Self::ParensBlock(_)
3296
| Self::BracedBlock(BracedBlock { full: true, .. })
3397
| Self::Ternary(Ternary { failure: None, .. })
3498
| Self::FunctionCall(_)
35-
| Self::ListInitialiser(ListInitialiser { full: true, .. }) => None,
99+
| Self::ListInitialiser(ListInitialiser { full: true, .. }) => CanMakeFnRes::None,
36100
Self::Unary(Unary { arg: child, .. })
37101
| Self::Binary(Binary { arg_r: child, .. })
38-
| Self::Ternary(Ternary { failure: Some(child), .. }) =>
39-
child.can_make_function().map(|depth| depth + 1),
102+
| Self::Ternary(Ternary { failure: Some(child), .. }) => child.can_make_function(),
40103
Self::FunctionArgsBuild(vec)
41104
| Self::ListInitialiser(ListInitialiser { elts: vec, .. })
42-
| Self::BracedBlock(BracedBlock { elts: vec, .. }) =>
43-
vec.last()?.can_make_function().map(|depth| depth + 1),
44-
Self::ControlFlow(ctrl) => ctrl.as_ast()?.can_make_function().map(|depth| depth + 1),
105+
| Self::BracedBlock(BracedBlock { elts: vec, .. }) => vec.last()?.can_make_function(),
106+
Self::ControlFlow(ctrl) => ctrl.as_ast()?.can_make_function(),
45107
}
46108
}
47109

@@ -50,7 +112,7 @@ impl MakeFunction for Ast {
50112
crate::errors::api::Print::custom_print(&format!("get last var of {self}"));
51113
if depth == 0 {
52114
if let Self::Variable(variable) = mem::take(self) {
53-
*self = Self::FunctionCall(FunctionCall { variable, arguments });
115+
*self = Self::FunctionCall(FunctionCall { arguments, variable });
54116
return;
55117
}
56118
unreachable!("must be variable at depth 0")

src/parser/symbols/blocks/recursion.rs

Lines changed: 64 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::lexer::api::Token;
1313
use crate::parser::keyword::control_flow::node::{
1414
switch_wanting_block, try_push_semicolon_control
1515
};
16-
use crate::parser::modifiers::functions::MakeFunction as _;
16+
use crate::parser::modifiers::functions::{CanMakeFnRes, MakeFunction as _};
1717
use crate::parser::modifiers::list_initialiser::{
1818
apply_to_last_list_initialiser, can_push_list_initialiser
1919
};
@@ -148,44 +148,74 @@ fn handle_parenthesis_open(
148148
tokens: &mut IntoIter<Token>,
149149
location: ErrorLocation,
150150
) -> Res<()> {
151-
if let Some(function_depth) = current.can_make_function() {
152-
let mut arguments_node = Ast::FunctionArgsBuild(vec![Ast::Empty]);
153-
p_state.push_ctrl_flow(false);
154-
parse_block(tokens, p_state, &mut arguments_node)?;
155-
if p_state.pop_ctrl_flow().is_none() {
156-
return Res::from(BlockType::Parenthesis.mismatched_err_end(location));
157-
}
158-
if p_state.pop_and_compare_block(&BlockType::Parenthesis) {
159-
if let Ast::FunctionArgsBuild(vec) = &mut arguments_node {
160-
let mut error = None;
161-
if vec.last().is_some_and(Ast::is_empty) {
162-
vec.pop();
163-
if !vec.is_empty() {
164-
error = Some(location.to_suggestion(
165-
"Found extra comma in function argument list. Please remove the comma.".to_owned(),
166-
));
167-
}
151+
match current.can_make_function() {
152+
CanMakeFnRes::CanMakeFn(variable_depth) =>
153+
make_function(current, p_state, tokens, location, variable_depth),
154+
CanMakeFnRes::None =>
155+
handle_non_function_parenthesis_open(current, p_state, tokens, location),
156+
CanMakeFnRes::TooDeep => Res::from(
157+
location.into_crash("Code to complex: AST to deep to fit depth in 32 bits.".to_owned()),
158+
),
159+
}
160+
}
161+
162+
/// Create a function for the found '('
163+
///
164+
/// Builds a function on a variable and adds its arguments.
165+
fn make_function(
166+
current: &mut Ast,
167+
p_state: &mut ParsingState,
168+
tokens: &mut IntoIter<Token>,
169+
location: ErrorLocation,
170+
variable_depth: u32,
171+
) -> Res<()> {
172+
let mut arguments_node = Ast::FunctionArgsBuild(vec![Ast::Empty]);
173+
p_state.push_ctrl_flow(false);
174+
parse_block(tokens, p_state, &mut arguments_node)?;
175+
if p_state.pop_ctrl_flow().is_none() {
176+
return Res::from(BlockType::Parenthesis.mismatched_err_end(location));
177+
}
178+
if p_state.pop_and_compare_block(&BlockType::Parenthesis) {
179+
if let Ast::FunctionArgsBuild(vec) = &mut arguments_node {
180+
let mut error = None;
181+
if vec.last().is_some_and(Ast::is_empty) {
182+
vec.pop();
183+
if !vec.is_empty() {
184+
error = Some(
185+
location.to_suggestion(
186+
"Found extra comma in function argument list. Please remove the comma."
187+
.to_owned(),
188+
),
189+
);
168190
}
169-
current.make_function(function_depth, mem::take(vec));
170-
parse_block(tokens, p_state, current).add_err(error)
171-
} else {
172-
unreachable!("a function args build cannot be dismissed as root");
173191
}
192+
current.make_function(variable_depth, mem::take(vec));
193+
parse_block(tokens, p_state, current).add_err(error)
174194
} else {
175-
Res::from(BlockType::Parenthesis.mismatched_err_end(location))
195+
unreachable!("a function args build cannot be dismissed as root");
176196
}
177197
} else {
178-
let mut parenthesized_block = Ast::Empty;
179-
parse_block(tokens, p_state, &mut parenthesized_block)?;
180-
parenthesized_block.fill();
181-
if p_state.pop_and_compare_block(&BlockType::Parenthesis) {
182-
current
183-
.push_block_as_leaf(ParensBlock::make_parens_ast(parenthesized_block))
184-
.map_err(|err| location.into_crash(err))?;
185-
parse_block(tokens, p_state, current)
186-
} else {
187-
Res::from(BlockType::Parenthesis.mismatched_err_end(location))
188-
}
198+
Res::from(BlockType::Parenthesis.mismatched_err_end(location))
199+
}
200+
}
201+
202+
/// Handles an opening '(', but when it can't be a function call.
203+
fn handle_non_function_parenthesis_open(
204+
current: &mut Ast,
205+
p_state: &mut ParsingState,
206+
tokens: &mut IntoIter<Token>,
207+
location: ErrorLocation,
208+
) -> Res<()> {
209+
let mut parenthesized_block = Ast::Empty;
210+
parse_block(tokens, p_state, &mut parenthesized_block)?;
211+
parenthesized_block.fill();
212+
if p_state.pop_and_compare_block(&BlockType::Parenthesis) {
213+
current
214+
.push_block_as_leaf(ParensBlock::make_parens_ast(parenthesized_block))
215+
.map_err(|err| location.into_crash(err))?;
216+
parse_block(tokens, p_state, current)
217+
} else {
218+
Res::from(BlockType::Parenthesis.mismatched_err_end(location))
189219
}
190220
}
191221

src/parser/variable/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use super::literal::Attribute;
3131
use super::modifiers::push::Push;
3232
use super::operators::api::OperatorConversions;
3333
use super::tree::api::{Ast, CanPush, PushAttribute};
34-
use crate::parser::modifiers::functions::MakeFunction;
34+
use crate::parser::modifiers::functions::{CanMakeFnRes, MakeFunction};
3535
use crate::utils::display;
3636

3737
/// Different variable cases
@@ -108,7 +108,7 @@ impl Variable {
108108
}
109109

110110
impl MakeFunction for Variable {
111-
fn can_make_function(&self) -> Option<u32> {
111+
fn can_make_function(&self) -> CanMakeFnRes {
112112
todo!()
113113
}
114114

0 commit comments

Comments
 (0)