diff options
| -rw-r--r-- | lib/xixanta/src/assembler.rs | 43 | ||||
| -rw-r--r-- | lib/xixanta/src/node.rs | 6 | ||||
| -rw-r--r-- | lib/xixanta/src/opcodes.rs | 10 | ||||
| -rw-r--r-- | lib/xixanta/src/parser.rs | 28 | ||||
| -rw-r--r-- | tests/bad_fallthrough.s | 10 | ||||
| -rw-r--r-- | tests/fallthrough.s | 6 |
6 files changed, 57 insertions, 46 deletions
diff --git a/lib/xixanta/src/assembler.rs b/lib/xixanta/src/assembler.rs index 5c43387..cb992d4 100644 --- a/lib/xixanta/src/assembler.rs +++ b/lib/xixanta/src/assembler.rs @@ -855,7 +855,20 @@ impl<'a> Assembler<'a> { errors.append(&mut ers); } } - + NodeType::Fallthrough => { + // We don't do much other than pushing a fake PendingNode with a + // 'bundle_index' value that will point to the next node. + let current = &mut self.mappings[self.current_mapping]; + self.pending.push(PendingNode { + mapping: self.current_mapping, + segment: self.current_segment, + context: self.context.name().to_string(), + bundle_index: current.segments[self.current_segment].bundles.len(), + node: node.to_owned(), + labels_seen: self.context.labels_seen(), + macro_context: self.macro_context.clone(), + }); + } _ => {} } @@ -890,10 +903,7 @@ impl<'a> Assembler<'a> { // // NOTE: this has to happen with a context switch, otherwise the // name resolution won't be accurate. - if matches!( - pn.node.node_type, - NodeType::Control(ControlType::Fallthrough) - ) { + if matches!(pn.node.node_type, NodeType::Fallthrough) { if let Err(e) = self.fallthrough(&pn) { errors.push(e); } @@ -970,16 +980,14 @@ impl<'a> Assembler<'a> { // If we are not in 'crunch' mode, then it's a bug. assert_eq!(self.stage, Stage::Crunching); - let node = pn.node.left.as_ref().unwrap(); - // If there was no "argument", then skip things altogether. The // programmer opted for an explicit fallthrough without further checks. - if node.value.is_empty() { + if pn.node.value.is_empty() { return Ok(()); } // If there is an argument, it has to be a valid address identifier. - if let Err(message) = node.value.is_valid_identifier(false) { + if let Err(message) = pn.node.value.is_valid_identifier(false) { return Err(Error { line: pn.node.value.line, message, @@ -995,7 +1003,7 @@ impl<'a> Assembler<'a> { // from the 'bundle_index' from the PendingNode, as it was pushed while // pointing to the "next" bundle. Hence, we just fetch that bundle and // get its address. - let target_address = self.evaluate_variable(node)?.value() as usize; + let target_address = self.evaluate_variable(&pn.node)?.value() as usize; let current = &self.mappings[pn.mapping].segments[pn.segment]; let Some(effective) = current.bundles.get(pn.bundle_index) else { return Err(Error { @@ -1935,21 +1943,6 @@ impl<'a> Assembler<'a> { NodeType::Control(ControlType::EndIf) => Ok(()), NodeType::Control(ControlType::IncludeSource) => Ok(()), NodeType::Control(ControlType::Echo(_)) => Ok(()), - NodeType::Control(ControlType::Fallthrough) => { - // We don't do much other than pushing a fake PendingNode with a - // 'bundle_index' value that will point to the next node. - let current = &mut self.mappings[self.current_mapping]; - self.pending.push(PendingNode { - mapping: self.current_mapping, - segment: self.current_segment, - context: self.context.name().to_string(), - bundle_index: current.segments[self.current_segment].bundles.len(), - node: node.to_owned(), - labels_seen: self.context.labels_seen(), - macro_context: self.macro_context.clone(), - }); - Ok(()) - } _ => Err(Error { line: node.value.line, message: format!( diff --git a/lib/xixanta/src/node.rs b/lib/xixanta/src/node.rs index 9fbe34c..5093ab3 100644 --- a/lib/xixanta/src/node.rs +++ b/lib/xixanta/src/node.rs @@ -166,7 +166,6 @@ pub enum ControlType { EndIf, Defined, Echo(EchoKind), - Fallthrough, } impl fmt::Display for ControlType { @@ -202,7 +201,6 @@ impl fmt::Display for ControlType { EchoKind::Warning => write!(f, ".warning"), EchoKind::Error => write!(f, ".error"), }, - ControlType::Fallthrough => write!(f, ".fallthrough"), } } } @@ -319,6 +317,9 @@ pub enum NodeType { /// A comment which is relevant for the current session. Comment(CommentType), + + /// Fallthrough pseudo-instruction. + Fallthrough, } impl fmt::Display for NodeType { @@ -333,6 +334,7 @@ impl fmt::Display for NodeType { NodeType::Literal => write!(f, "literal"), NodeType::Label => write!(f, "label"), NodeType::Call => write!(f, "call"), + NodeType::Fallthrough => write!(f, "fallthrough"), NodeType::Operation(op) => match op { OperationType::Add => write!(f, "addition"), OperationType::Sub => write!(f, "subtraction"), diff --git a/lib/xixanta/src/opcodes.rs b/lib/xixanta/src/opcodes.rs index bd3c207..48e4fca 100644 --- a/lib/xixanta/src/opcodes.rs +++ b/lib/xixanta/src/opcodes.rs @@ -1999,16 +1999,6 @@ pub static CONTROL_FUNCTIONS: LazyLock<HashMap<String, Control>> = LazyLock::new only_string: true, }, ); - functions.insert( - String::from(".fallthrough"), - Control { - control_type: ControlType::Fallthrough, - has_identifier: Some(false), - required_args: Some((0, 0)), - touches_context: false, - only_string: false, - }, - ); functions }); diff --git a/lib/xixanta/src/parser.rs b/lib/xixanta/src/parser.rs index 7a7e3f0..e2e31dc 100644 --- a/lib/xixanta/src/parser.rs +++ b/lib/xixanta/src/parser.rs @@ -766,9 +766,12 @@ impl Parser { // Parse statements which are neither an instruction nor an assignment. This // includes stuff like control statements or macro calls. fn parse_other(&mut self, line: &str, id: PString) -> Result<(), Vec<Error>> { - // This is either a control statement (i.e. starts with '.') or a macro call. + // This is either a control statement (i.e. starts with '.'), a + // __fallthrough__, or a macro call. let node = if id.value.starts_with('.') { self.parse_control(id, line, 0)? + } else if id.value == "__fallthrough__" { + self.parse_fallthrough(line)? } else { let args = self.parse_arguments(line, 0)?; PNode { @@ -1642,6 +1645,29 @@ impl Parser { }) } + // Parse a fallthrough statement. + fn parse_fallthrough(&mut self, line: &str) -> Result<PNode, Error> { + // Skip whitespaces and fetch the identifier. + self.skip_whitespace(line); + let identifier = self.parse_identifier(line, false)?.0; + + // If there was something else other than the identifier, it's a parsing + // error. + let rest = line.get(self.offset..).unwrap_or("").trim(); + if !rest.is_empty() { + return Err(self.parser_error("too many arguments for __fallthrough__")); + } + + Ok(PNode { + node_type: NodeType::Fallthrough, + value: identifier, + left: None, + right: None, + args: None, + source: self.current_source, + }) + } + // Returns a NodeType::Literal node with whatever could be parsed // considering the given `line` which starts with the given `symbol`. The // recursivity `level` is also provided as it will call again diff --git a/tests/bad_fallthrough.s b/tests/bad_fallthrough.s index ac17ade..80c35c3 100644 --- a/tests/bad_fallthrough.s +++ b/tests/bad_fallthrough.s @@ -8,20 +8,20 @@ .proc foo lda #0 - .fallthrough bar + __fallthrough__ bar .endproc -.fallthrough bar -.fallthrough other +__fallthrough__ bar +__fallthrough__ other lda #0 .proc bar lda #0 - .fallthrough other + __fallthrough__ other .endproc .proc other rts .endproc -.fallthrough other +__fallthrough__ other diff --git a/tests/fallthrough.s b/tests/fallthrough.s index 2340ee8..6e95b5e 100644 --- a/tests/fallthrough.s +++ b/tests/fallthrough.s @@ -8,14 +8,14 @@ .proc foo lda #0 - .fallthrough bar + __fallthrough__ bar .endproc -.fallthrough bar +__fallthrough__ bar .proc bar lda #0 - .fallthrough other + __fallthrough__ other .endproc .proc other |
