From 1b0d50aa2f91a795870eeecc01371ce262d6f54c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miquel=20Sabat=C3=A9=20Sol=C3=A0?= Date: Tue, 29 Oct 2024 14:49:44 +0100 Subject: [PATCH] Don't rely on the identifier for control identity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Miquel Sabaté Solà --- lib/xixanta/src/assembler.rs | 94 ++++++++++++++++++------------------ lib/xixanta/src/node.rs | 30 ++++++++++-- lib/xixanta/src/opcodes.rs | 30 ++++++------ lib/xixanta/src/parser.rs | 70 ++++++++++++++++++++++----- 4 files changed, 148 insertions(+), 76 deletions(-) diff --git a/lib/xixanta/src/assembler.rs b/lib/xixanta/src/assembler.rs index e614b42..0f164d7 100644 --- a/lib/xixanta/src/assembler.rs +++ b/lib/xixanta/src/assembler.rs @@ -1,7 +1,7 @@ use crate::context::Context; use crate::errors::{Error, EvalError}; use crate::mapping::Segment; -use crate::node::{NodeType, PNode, PString}; +use crate::node::{ControlType, NodeType, PNode, PString}; use crate::opcodes::{AddressingMode, INSTRUCTIONS}; use crate::parser::Parser; use std::cmp::Ordering; @@ -145,7 +145,7 @@ impl Assembler { let mut current_macro = None; for (idx, node) in nodes.iter().enumerate() { - match node.node_type { + match &node.node_type { NodeType::Label => { if let Err(err) = self.context @@ -174,43 +174,46 @@ impl Assembler { Err(e) => errors.push(Error::Eval(e)), } } - NodeType::Control => { + NodeType::Control(control_type) => { // TODO: prevent nesting of control statements depending on // a definition (e.g. .macro's cannot be nested inside of // another control statement, but .if yes). - let id = node.value.value.as_str(); - - if id == ".macro" { - // TODO: macros are only on the global scope. - // - // TODO: boy this is ugly. In fact, this stupid shit if - // current_macro might not be relevant anymore. - current_macro = Some(&node.left.as_ref().unwrap().value); - // TODO: watch out for weird shit on the name of arguments. - self.macros - .entry(node.left.as_ref().unwrap().value.value.clone()) - .or_insert(Macro { - nodes: Range { - start: idx + 1, - end: idx + 1, - }, - args: node - .args - .clone() - .unwrap_or_default() - .into_iter() - .map(|a| a.value) - .collect::>(), - }); - } else if id == ".endmacro" { - // TODO: if m.nodes.start < idx - 1 => empty macro - if let Some(name) = current_macro { + match control_type { + ControlType::StartMacro => { + // TODO: macros are only on the global scope. + // + // TODO: boy this is ugly. In fact, this stupid shit if + // current_macro might not be relevant anymore. + current_macro = Some(&node.left.as_ref().unwrap().value); + // TODO: watch out for weird shit on the name of arguments. self.macros - .entry(name.value.clone()) - .and_modify(|m| m.nodes.end = idx - 1); + .entry(node.left.as_ref().unwrap().value.value.clone()) + .or_insert(Macro { + nodes: Range { + start: idx + 1, + end: idx + 1, + }, + args: node + .args + .clone() + .unwrap_or_default() + .into_iter() + .map(|a| a.value) + .collect::>(), + }); + } + ControlType::EndMacro => { + // TODO: if m.nodes.start < idx - 1 => empty macro + + if let Some(name) = current_macro { + self.macros + .entry(name.value.clone()) + .and_modify(|m| m.nodes.end = idx - 1); + } + current_macro = None; } - current_macro = None; + _ => {} } if let Err(err) = self.context.change_context(node) { // TODO: forbid if inside_macro @@ -271,7 +274,7 @@ impl Assembler { } } } - NodeType::Control => { + NodeType::Control(_) => { if let Err(e) = self.evaluate_control_statement(node) { errors.push(Error::Eval(e)); } @@ -422,7 +425,7 @@ impl Assembler { match node.node_type { NodeType::Instruction => Ok(self.evaluate_instruction(node)?), NodeType::Literal => Ok(self.evaluate_literal(node)?), - NodeType::Control => Ok(self.evaluate_control_expression(node)?), + NodeType::Control(_) => Ok(self.evaluate_control_expression(node)?), NodeType::Value => match self.literal_mode { Some(LiteralMode::Hexadecimal) => Ok(self.evaluate_hexadecimal(node)?), Some(LiteralMode::Binary) => Ok(self.evaluate_binary(node)?), @@ -722,31 +725,30 @@ impl Assembler { // Otherwise, check the function that could act as a statement that // produces bundles. - let function = node.value.value.as_str(); - match function { - ".byte" | ".db" => self.push_evaluated_arguments(node, 1), - ".addr" | ".word" | ".dw" => self.push_evaluated_arguments(node, 2), + match node.node_type { + NodeType::Control(ControlType::Byte) => self.push_evaluated_arguments(node, 1), + NodeType::Control(ControlType::Addr) | NodeType::Control(ControlType::Word) => { + self.push_evaluated_arguments(node, 2) + } _ => Err(EvalError { line: node.value.line, message: format!( "cannot handle control statement '{}' in this context", - function + node.value.value ), }), } } fn evaluate_control_expression(&mut self, node: &PNode) -> Result { - let function = node.value.value.to_lowercase(); - - match function.as_str() { - ".hibyte" => self.evaluate_byte(node, true), - ".lobyte" => self.evaluate_byte(node, false), + match node.node_type { + NodeType::Control(ControlType::Hibyte) => self.evaluate_byte(node, true), + NodeType::Control(ControlType::Lobyte) => self.evaluate_byte(node, false), _ => Err(EvalError { line: node.value.line, message: format!( "cannot handle control statement '{}' as an expression in this context", - function + node.value.value ), }), } diff --git a/lib/xixanta/src/node.rs b/lib/xixanta/src/node.rs index a28d625..c787394 100644 --- a/lib/xixanta/src/node.rs +++ b/lib/xixanta/src/node.rs @@ -82,6 +82,24 @@ impl PString { } } +/// The type of control function being used. Use this enum in order to detect +/// which control function was detected instead of the node value. +#[derive(Debug, Clone, PartialEq)] +pub enum ControlType { + Hibyte, + Lobyte, + StartMacro, + EndMacro, + StartProc, + EndProc, + StartScope, + EndScope, + Segment, + Byte, + Word, + Addr, +} + /// The PNode type. #[derive(Debug, Clone, PartialEq)] pub enum NodeType { @@ -106,10 +124,12 @@ pub enum NodeType { Assignment, /// A control statement (e.g. ".proc foo"). The `value` string contains the - /// name of the function, the `left` an optional identifier (e.g. the "foo" - /// on ".proc foo"), and the `args` contain any possible arguments that have - /// been passed to this control statement. - Control, + /// name of the function as it was provided (use the `ControlType` value of + /// the enum to detect which function was exactly provided), the `left` an + /// optional identifier (e.g. the "foo" on ".proc foo"), and the `args` + /// contain any possible arguments that have been passed to this control + /// statement. + Control(ControlType), /// A literal expression, that is, something that starts with '#', '%' or /// '$'. The `left` node contains the inner expression. @@ -130,7 +150,7 @@ impl fmt::Display for NodeType { NodeType::Instruction => write!(f, "instruction"), NodeType::Indirection => write!(f, "indirection"), NodeType::Assignment => write!(f, "assignment"), - NodeType::Control => write!(f, "control function"), + NodeType::Control(_) => write!(f, "control function"), NodeType::Literal => write!(f, "literal"), NodeType::Label => write!(f, "label"), NodeType::Call => write!(f, "call"), diff --git a/lib/xixanta/src/opcodes.rs b/lib/xixanta/src/opcodes.rs index 0e7f379..4e336ba 100644 --- a/lib/xixanta/src/opcodes.rs +++ b/lib/xixanta/src/opcodes.rs @@ -1,3 +1,4 @@ +use crate::node::ControlType; use std::collections::HashMap; use std::fmt; @@ -55,6 +56,7 @@ pub struct ShortEntry { #[derive(Debug)] pub struct Control { + pub control_type: ControlType, pub has_identifier: bool, pub required_args: Option, pub touches_context: bool, @@ -714,20 +716,20 @@ lazy_static! { pub static ref CONTROL_FUNCTIONS: HashMap = { let mut functions = HashMap::new(); - functions.insert(String::from(".hibyte"), Control { has_identifier: false, required_args: Some(1), touches_context: false }); - functions.insert(String::from(".lobyte"), Control { has_identifier: false, required_args: Some(1), touches_context: false }); - functions.insert(String::from(".macro"), Control { has_identifier: true, required_args: None, touches_context: true }); - functions.insert(String::from(".proc"), Control { has_identifier: true, required_args: Some(0), touches_context: true }); - functions.insert(String::from(".scope"), Control { has_identifier: true, required_args: Some(0), touches_context: true }); - functions.insert(String::from(".endscope"), Control { has_identifier: false, required_args: Some(0), touches_context: true }); - functions.insert(String::from(".endproc"), Control { has_identifier: false, required_args: Some(0), touches_context: true }); - functions.insert(String::from(".endmacro"), Control { has_identifier: false, required_args: Some(0), touches_context: true }); - functions.insert(String::from(".segment"), Control { has_identifier: false, required_args: Some(1), touches_context: true }); - functions.insert(String::from(".byte"), Control { has_identifier: false, required_args: None, touches_context: false }); - functions.insert(String::from(".db"), Control { has_identifier: false, required_args: None, touches_context: false }); - functions.insert(String::from(".word"), Control { has_identifier: false, required_args: None, touches_context: false }); - functions.insert(String::from(".dw"), Control { has_identifier: false, required_args: None, touches_context: false }); - functions.insert(String::from(".addr"), Control { has_identifier: false, required_args: None, touches_context: false }); + functions.insert(String::from(".hibyte"), Control { control_type: ControlType::Hibyte, has_identifier: false, required_args: Some(1), touches_context: false }); + functions.insert(String::from(".lobyte"), Control { control_type: ControlType::Lobyte, has_identifier: false, required_args: Some(1), touches_context: false }); + functions.insert(String::from(".macro"), Control { control_type: ControlType::StartMacro, has_identifier: true, required_args: None, touches_context: true }); + functions.insert(String::from(".proc"), Control { control_type: ControlType::StartProc, has_identifier: true, required_args: Some(0), touches_context: true }); + functions.insert(String::from(".scope"), Control { control_type: ControlType::StartScope, has_identifier: true, required_args: Some(0), touches_context: true }); + functions.insert(String::from(".endscope"), Control { control_type: ControlType::EndScope, has_identifier: false, required_args: Some(0), touches_context: true }); + functions.insert(String::from(".endproc"), Control { control_type: ControlType::EndProc, has_identifier: false, required_args: Some(0), touches_context: true }); + functions.insert(String::from(".endmacro"), Control { control_type: ControlType::EndMacro, has_identifier: false, required_args: Some(0), touches_context: true }); + functions.insert(String::from(".segment"), Control { control_type: ControlType::Segment, has_identifier: false, required_args: Some(1), touches_context: true }); + functions.insert(String::from(".byte"), Control { control_type: ControlType::Byte, has_identifier: false, required_args: None, touches_context: false }); + functions.insert(String::from(".db"), Control { control_type: ControlType::Byte, has_identifier: false, required_args: None, touches_context: false }); + functions.insert(String::from(".word"), Control { control_type: ControlType::Word, has_identifier: false, required_args: None, touches_context: false }); + functions.insert(String::from(".dw"), Control { control_type: ControlType::Word, has_identifier: false, required_args: None, touches_context: false }); + functions.insert(String::from(".addr"), Control { control_type: ControlType::Addr, has_identifier: false, required_args: None, touches_context: false }); functions }; diff --git a/lib/xixanta/src/parser.rs b/lib/xixanta/src/parser.rs index 2c20369..9e7b7d5 100644 --- a/lib/xixanta/src/parser.rs +++ b/lib/xixanta/src/parser.rs @@ -703,11 +703,13 @@ impl Parser { fn parse_control(&mut self, id: PString, line: &str) -> Result { let mut left = None; let required; + let node_type; // Ensure that this is a function that we know of. In the past this was // not done and it brought too many problems that made the more // "abstract" way of handling this just too complicated. if let Some(control) = CONTROL_FUNCTIONS.get(&id.value.to_lowercase()) { + node_type = control.control_type.clone(); required = control.required_args; // If this control function has an identifier (e.g. `.macro @@ -740,7 +742,7 @@ impl Parser { } Ok(PNode { - node_type: NodeType::Control, + node_type: NodeType::Control(node_type), value: id, left, right: None, @@ -815,6 +817,7 @@ impl Parser { #[cfg(test)] mod tests { use super::*; + use crate::node::ControlType; fn assert_one_valid(parser: &mut Parser, line: &str) { assert!(parser.parse(line.as_bytes()).is_ok()); @@ -1356,7 +1359,12 @@ mod tests { assert!(parser.parse(line.as_bytes()).is_ok()); let node = parser.nodes.last().unwrap(); - assert_node(node, NodeType::Control, line, ".endmacro"); + assert_node( + node, + NodeType::Control(ControlType::EndMacro), + line, + ".endmacro", + ); assert!(node.left.is_none()); assert!(node.right.is_none()); assert!(node.args.is_none()); @@ -1378,7 +1386,12 @@ mod tests { assert!(parser.parse(line.as_bytes()).is_ok()); let node = parser.nodes.last().unwrap(); - assert_node(node, NodeType::Control, line, ".hibyte"); + assert_node( + node, + NodeType::Control(ControlType::Hibyte), + line, + ".hibyte", + ); assert!(node.left.is_none()); assert!(node.right.is_none()); @@ -1403,7 +1416,7 @@ mod tests { assert!(parser.parse(line.as_bytes()).is_ok()); let node = parser.nodes.last().unwrap(); - assert_node(node, NodeType::Control, line, ".byte"); + assert_node(node, NodeType::Control(ControlType::Byte), line, ".byte"); assert!(node.left.is_none()); assert!(node.right.is_none()); @@ -1428,7 +1441,12 @@ mod tests { assert!(parser.parse(line.as_bytes()).is_ok()); let node = parser.nodes.last().unwrap(); - assert_node(node, NodeType::Control, line, ".scope"); + assert_node( + node, + NodeType::Control(ControlType::StartScope), + line, + ".scope", + ); assert!(node.right.is_none()); assert!(node.args.is_none()); @@ -1452,7 +1470,12 @@ mod tests { assert!(parser.parse(line.as_bytes()).is_ok()); let node = parser.nodes.last().unwrap(); - assert_node(node, NodeType::Control, line, ".macro"); + assert_node( + node, + NodeType::Control(ControlType::StartMacro), + line, + ".macro", + ); assert!(node.right.is_none()); let left = node.left.clone().unwrap(); @@ -1479,7 +1502,12 @@ mod tests { assert!(parser.parse(line.as_bytes()).is_ok()); let node = parser.nodes.last().unwrap(); - assert_node(node, NodeType::Control, line, ".macro"); + assert_node( + node, + NodeType::Control(ControlType::StartMacro), + line, + ".macro", + ); assert!(node.right.is_none()); let left = node.left.clone().unwrap(); @@ -1522,7 +1550,12 @@ mod tests { assert!(left.args.is_none()); let control = left.left.clone().unwrap(); - assert_node(&control, NodeType::Control, line, ".hibyte"); + assert_node( + &control, + NodeType::Control(ControlType::Hibyte), + line, + ".hibyte", + ); assert!(control.left.is_none()); assert!(control.right.is_none()); @@ -1558,7 +1591,12 @@ mod tests { assert!(left.args.is_none()); let control = left.left.clone().unwrap(); - assert_node(&control, NodeType::Control, line, ".hibyte"); + assert_node( + &control, + NodeType::Control(ControlType::Hibyte), + line, + ".hibyte", + ); assert!(control.left.is_none()); assert!(control.right.is_none()); @@ -1597,7 +1635,12 @@ mod tests { assert!(left.args.is_none()); let control = left.left.clone().unwrap(); - assert_node(&control, NodeType::Control, line, ".hibyte"); + assert_node( + &control, + NodeType::Control(ControlType::Hibyte), + line, + ".hibyte", + ); assert!(control.left.is_none()); assert!(control.right.is_none()); @@ -1633,7 +1676,12 @@ mod tests { assert!(left.args.is_none()); let control = left.left.clone().unwrap(); - assert_node(&control, NodeType::Control, line, ".hibyte"); + assert_node( + &control, + NodeType::Control(ControlType::Hibyte), + line, + ".hibyte", + ); assert!(control.left.is_none()); assert!(control.right.is_none());