aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mikisabate@gmail.com>2025-01-20 08:01:54 +0100
committerMiquel Sabaté Solà <mikisabate@gmail.com>2025-01-20 08:01:54 +0100
commitf00ead262076a1cf0429d6fb0de8671f6376af57 (patch)
treedf5bad75e0e90956bc9461be251921d985da779d
parente4e71cda739c9af83302af85520518b838739f91 (diff)
downloadtools.nes-f00ead262076a1cf0429d6fb0de8671f6376af57.tar.gz
tools.nes-f00ead262076a1cf0429d6fb0de8671f6376af57.zip
Track the level of recursive calls
This is in preparation to some heavy lifting that is to be done to the parser so it more properly handles nested expressions, but in general it's a good idea to have some limits to functions that expect to be called recursively quite heavily. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
-rw-r--r--lib/xixanta/src/parser.rs53
1 files changed, 35 insertions, 18 deletions
diff --git a/lib/xixanta/src/parser.rs b/lib/xixanta/src/parser.rs
index e47f115..dbe5fbe 100644
--- a/lib/xixanta/src/parser.rs
+++ b/lib/xixanta/src/parser.rs
@@ -474,7 +474,7 @@ impl Parser {
None
} else {
self.offset = 0;
- Some(Box::new(self.parse_expression(right_str)?))
+ Some(Box::new(self.parse_expression(right_str, 0)?))
};
// If we were in indirect addressing mode, then there's some juggling we
@@ -524,7 +524,7 @@ impl Parser {
self.skip_whitespace(right_str);
// And finally parse the right arm for the global instruction.
- Some(Box::new(self.parse_expression(right_str)?))
+ Some(Box::new(self.parse_expression(right_str, 0)?))
};
}
@@ -561,7 +561,7 @@ impl Parser {
return Err(self.parser_error("incomplete assignment"));
};
self.offset = 0;
- let left = self.parse_expression(rest)?;
+ let left = self.parse_expression(rest, 0)?;
// And push the node.
self.nodes.last_mut().unwrap().push(PNode {
@@ -581,7 +581,7 @@ impl Parser {
fn parse_other(&mut self, line: &str, id: PString) -> Result<(), Vec<Error>> {
// The main job of this function is to parse the expression and
// afterwards deal with corner cases. So, let's first just parse this.
- let node = self.parse_expression_with_identifier(id, line)?;
+ let node = self.parse_expression_with_identifier(id, line, 0)?;
// If this was an .include statement we have to handle it now as this is
// not a regular statement but more like a preprocessor statement which
@@ -877,7 +877,7 @@ impl Parser {
// Parse the argument, which is trimmed down from the line and hence
// the offset needs to be reset.
self.offset = 0;
- args.push(self.parse_expression(arg)?);
+ args.push(self.parse_expression(arg, 0)?);
// After the parsing is done for the current argument, move both
// `self.offset` and `self.column` right after the end of the
@@ -918,7 +918,7 @@ impl Parser {
// Parse the expression that we can get from the current offset to the
// computed end.
- let expr = self.parse_expression(str);
+ let expr = self.parse_expression(str, 0);
// Set the `offset` and `column` to the end of the line that is shared
// with the caller.
@@ -999,7 +999,11 @@ impl Parser {
// Parses the given line by assuming it's an expression under parenthesis.
// This function then grabs whatever is inside of these parenthesis and
// parses the expression inside of them.
- fn extract_parenthesized_expression(&mut self, line: &str) -> Result<PNode, Error> {
+ fn extract_parenthesized_expression(
+ &mut self,
+ line: &str,
+ level: usize,
+ ) -> Result<PNode, Error> {
// Skip '(' character and whitespace characters in between.
self.next();
self.skip_whitespace(line);
@@ -1010,7 +1014,7 @@ impl Parser {
// And return what you can parse from the inner expression.
self.offset = 0;
- self.parse_expression(l)
+ self.parse_expression(l, level + 1)
}
// Consumes the given line by assuming is a string in double quotes.
@@ -1056,7 +1060,12 @@ impl Parser {
// Returns a node for the given `node_type` unary operation, where the given
// `line` is the whole expression (including the unary operator).
- fn parse_unary_operation(&mut self, node_type: NodeType, line: &str) -> Result<PNode, Error> {
+ fn parse_unary_operation(
+ &mut self,
+ node_type: NodeType,
+ line: &str,
+ level: usize,
+ ) -> Result<PNode, Error> {
// Skip operator and whitespaces.
let start = self.column;
self.next();
@@ -1065,7 +1074,7 @@ impl Parser {
// Fetch the right side of the operator.
let right_str = line.get(self.offset..).unwrap_or_default().trim_end();
self.offset = 0;
- let right = self.parse_expression(right_str)?;
+ let right = self.parse_expression(right_str, level + 1)?;
Ok(PNode {
node_type,
@@ -1087,11 +1096,18 @@ impl Parser {
// `line` (e.g. the line might not be a full line but rather a limited range
// and the offset has been set accordingly). Returns a new node for the
// expression at hand.
- fn parse_expression(&mut self, line: &str) -> Result<PNode, Error> {
+ fn parse_expression(&mut self, line: &str, level: usize) -> Result<PNode, Error> {
+ // Avoid stack overflows from too many recursive calls for expressions
+ // that are too nested. This mainly requires fuzzy testing to get to
+ // this point, but let's be safe about this anyways.
+ if level >= 16 {
+ return Err(self.parser_error("there are too many nested expressions"));
+ }
+
// Cases where fetching an "identifier" is really not needed.
let first = line.chars().next().unwrap_or_default();
if first == '(' {
- return self.extract_parenthesized_expression(line);
+ return self.extract_parenthesized_expression(line, level);
} else if let Some(node_type) = self.get_unary_from_line(line) {
// Only treat this as a unary operator if the next character is not
// another unary operator (e.g. disambiguate between '<<' and '<').
@@ -1099,7 +1115,7 @@ impl Parser {
.get_unary_from_line(line.get(1..).unwrap_or(""))
.is_none()
{
- return self.parse_unary_operation(node_type, line);
+ return self.parse_unary_operation(node_type, line, level);
}
}
@@ -1111,7 +1127,7 @@ impl Parser {
if nt == NodeType::Label {
Err(self.parser_error("not expecting a label defined here"))
} else {
- self.parse_expression_with_identifier(id, line)
+ self.parse_expression_with_identifier(id, line, level)
}
}
@@ -1182,6 +1198,7 @@ impl Parser {
&mut self,
id: PString,
line: &str,
+ level: usize,
) -> Result<PNode, Error> {
// Reaching this condition is usually a bad sign, but there is so many
// ways in which it could go wrong, that an `assert!` wouldn't be fair
@@ -1211,7 +1228,7 @@ impl Parser {
}
}
- self.parse_literal(id, line)
+ self.parse_literal(id, line, level)
} else if start == '\'' {
self.parse_char(id)
} else {
@@ -1245,7 +1262,7 @@ impl Parser {
// The right node of the operation will be the parsed expression
// of the string we just got right of the operator.
self.offset = 0;
- let right = self.parse_expression(right_str)?;
+ let right = self.parse_expression(right_str, level)?;
return Ok(PNode {
node_type,
@@ -1365,7 +1382,7 @@ impl Parser {
// Returns a NodeType::Literal node with whatever could be parsed
// considering the given `id` and rest of the `line`.
- fn parse_literal(&mut self, id: PString, line: &str) -> Result<PNode, Error> {
+ fn parse_literal(&mut self, id: PString, line: &str, level: usize) -> Result<PNode, Error> {
// Force the column to point to the literal character just in case
// of expressions like '#.hibyte'. Then skip whitespaces for super
// ugly statements such as '# 20'. This is ugly but we should permit
@@ -1392,7 +1409,7 @@ impl Parser {
// Just fetch the inner expression and return the literal node.
self.offset = 0;
- let left = self.parse_expression(inner)?;
+ let left = self.parse_expression(inner, level + 1)?;
Ok(PNode {
node_type: NodeType::Literal,