Skip to content

Commit

Permalink
Don't rely on the identifier for control identity
Browse files Browse the repository at this point in the history
Signed-off-by: Miquel Sabaté Solà <[email protected]>
  • Loading branch information
mssola committed Oct 29, 2024
1 parent fe84c9c commit 1b0d50a
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 76 deletions.
94 changes: 48 additions & 46 deletions lib/xixanta/src/assembler.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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::<Vec<_>>(),
});
} 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::<Vec<_>>(),
});
}
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
Expand Down Expand Up @@ -271,7 +274,7 @@ impl Assembler {
}
}
}
NodeType::Control => {
NodeType::Control(_) => {
if let Err(e) = self.evaluate_control_statement(node) {
errors.push(Error::Eval(e));
}
Expand Down Expand Up @@ -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)?),
Expand Down Expand Up @@ -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<Bundle, EvalError> {
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
),
}),
}
Expand Down
30 changes: 25 additions & 5 deletions lib/xixanta/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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.
Expand All @@ -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"),
Expand Down
30 changes: 16 additions & 14 deletions lib/xixanta/src/opcodes.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::node::ControlType;
use std::collections::HashMap;
use std::fmt;

Expand Down Expand Up @@ -55,6 +56,7 @@ pub struct ShortEntry {

#[derive(Debug)]
pub struct Control {
pub control_type: ControlType,
pub has_identifier: bool,
pub required_args: Option<usize>,
pub touches_context: bool,
Expand Down Expand Up @@ -714,20 +716,20 @@ lazy_static! {
pub static ref CONTROL_FUNCTIONS: HashMap<String, Control> = {
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
};
Expand Down
Loading

0 comments on commit 1b0d50a

Please sign in to comment.