aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mikisabate@gmail.com>2025-01-05 09:11:05 +0100
committerMiquel Sabaté Solà <mikisabate@gmail.com>2025-01-05 09:11:05 +0100
commitcb8af87b54c475ec5adcb6058f6656d4f27d4b13 (patch)
tree800ec928283e504f4d72bd72db3cd33d7909ea67 /lib
parentec8b709fa24c3386dae919cf0c33a51468efbae5 (diff)
downloadtools.nes-cb8af87b54c475ec5adcb6058f6656d4f27d4b13.tar.gz
tools.nes-cb8af87b54c475ec5adcb6058f6656d4f27d4b13.zip
parser: Move out the parsing of control statements
The parsing of control statements have become more complex since the introduction of block bodies in 1f8a6becc7cd ("parser: Implement block bodies"); so it makes sense to move it into its own thing and keep `parse_statement` more clear. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/xixanta/src/parser.rs113
1 files changed, 63 insertions, 50 deletions
diff --git a/lib/xixanta/src/parser.rs b/lib/xixanta/src/parser.rs
index 0de0bd8..8429c2e 100644
--- a/lib/xixanta/src/parser.rs
+++ b/lib/xixanta/src/parser.rs
@@ -21,10 +21,18 @@ pub struct Parser {
/// The nodes that have been evaluated for the current parsing session. You
/// can count on this vector to be filled after calling
- /// `parser::Parser::parse`.
- // pub nodes: Vec<PNode>,
+ /// `parser::Parser::parse`. Note that it's a vector of vectors to handle
+ /// statements which contain a block (e.g. '.macro'). In the end of the
+ /// parsing session, though, this vector of vectors is guaranteed to have
+ /// len() == 1, which means that there's only one code "layer" in the end
+ /// because the other blocks have been closed (or forced to be closed on bad
+ /// .end placements).
nodes: Vec<Vec<PNode>>,
+ /// This is a stack of node types that we are expecting for closing the
+ /// currently open inner block. This is done this way to guarantee that end
+ /// statements (e.g. '.endproc') go with their respective start ones (e.g.
+ /// '.proc'), and they don't close another block.
bodies: Vec<NodeType>,
}
@@ -313,54 +321,7 @@ impl Parser {
if line.contains('=') {
self.parse_assignment(line, id)
} else {
- // TODO: move out into its own thing...
- let node = self.parse_expression_with_identifier(id, line)?;
- let node_type = node.node_type.clone();
- let body_type = node.body_type();
-
- match body_type {
- NodeBodyType::Starts => {
- self.bodies.push(node_type.closing_type().unwrap());
- self.nodes.last_mut().unwrap().push(node);
- self.nodes.push(vec![]);
- }
- NodeBodyType::Ends => {
- let expected_close = match self.bodies.pop() {
- Some(ec) => ec,
- None => {
- return Err(self.parser_error(
- format!("unexpected '{}'", node_type).as_str(),
- ))
- }
- };
- if node_type != expected_close {
- return Err(self.parser_error(
- format!(
- "expecting '{}', found '{}'",
- expected_close, node_type
- )
- .as_str(),
- ));
- }
-
- // Note that empty bodies are possible. This is left
- // to the caller (e.g. assembler) to decide whether
- // it makes sense or not.
- let nodes = self.nodes.pop().unwrap();
- self.nodes.last_mut().unwrap().last_mut().unwrap().right =
- Some(Box::new(PNode {
- node_type: NodeType::ControlBody,
- value: PString::default(),
- left: None,
- right: None,
- args: Some(nodes),
- }));
- self.nodes.last_mut().unwrap().push(node);
- }
- NodeBodyType::None => self.nodes.last_mut().unwrap().push(node),
- }
-
- Ok(())
+ self.parse_other(line, id)
}
}
}
@@ -535,6 +496,58 @@ impl Parser {
Ok(())
}
+ // Parse statements which are neither an instruction nor an assignment. This
+ // includes stuff like control statements.
+ fn parse_other(&mut self, line: &str, id: PString) -> Result<(), ParseError> {
+ let node = self.parse_expression_with_identifier(id, line)?;
+ let node_type = node.node_type.clone();
+ let body_type = node.body_type();
+
+ // The given statement might be a start/end one (e.g. '.proc' and
+ // '.endproc'). In these cases there are some things to handle besides
+ // pushing the node into the current level of nodes.
+ match body_type {
+ NodeBodyType::Starts => {
+ self.bodies.push(node_type.closing_type().unwrap());
+ self.nodes.last_mut().unwrap().push(node);
+ self.nodes.push(vec![]);
+ }
+ NodeBodyType::Ends => {
+ // Pop out which start statement was last seen and check that it
+ // makes sense to the end statement we are parsing now.
+ let expected_close = match self.bodies.pop() {
+ Some(ec) => ec,
+ None => {
+ return Err(
+ self.parser_error(format!("unexpected '{}'", node_type).as_str())
+ )
+ }
+ };
+ if node_type != expected_close {
+ return Err(self.parser_error(
+ format!("expecting '{}', found '{}'", expected_close, node_type).as_str(),
+ ));
+ }
+
+ // Note that empty bodies are possible. This is left
+ // to the caller (e.g. assembler) to decide whether
+ // it makes sense or not.
+ let nodes = self.nodes.pop().unwrap();
+ self.nodes.last_mut().unwrap().last_mut().unwrap().right = Some(Box::new(PNode {
+ node_type: NodeType::ControlBody,
+ value: PString::default(),
+ left: None,
+ right: None,
+ args: Some(nodes),
+ }));
+ self.nodes.last_mut().unwrap().push(node);
+ }
+ NodeBodyType::None => self.nodes.last_mut().unwrap().push(node),
+ }
+
+ Ok(())
+ }
+
// Parse any possible arguments for the given `line`. The offset is supposed
// to be at a point where arguments might appear, either between parens or
// not.