aboutsummaryrefslogtreecommitdiff
path: root/lib/xixanta
diff options
context:
space:
mode:
Diffstat (limited to 'lib/xixanta')
-rw-r--r--lib/xixanta/src/assembler.rs94
-rw-r--r--lib/xixanta/src/context.rs13
-rw-r--r--lib/xixanta/src/node.rs30
-rw-r--r--lib/xixanta/src/opcodes.rs30
-rw-r--r--lib/xixanta/src/parser.rs70
5 files changed, 156 insertions, 81 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::<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
@@ -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<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
),
}),
}
diff --git a/lib/xixanta/src/context.rs b/lib/xixanta/src/context.rs
index bccac12..6933085 100644
--- a/lib/xixanta/src/context.rs
+++ b/lib/xixanta/src/context.rs
@@ -1,6 +1,6 @@
use crate::assembler::Bundle;
use crate::errors::{ContextError, ContextErrorReason};
-use crate::node::{PNode, PString};
+use crate::node::{ControlType, NodeType, PNode, PString};
use crate::opcodes::CONTROL_FUNCTIONS;
use std::collections::HashMap;
@@ -111,16 +111,19 @@ impl Context {
}
// And push/pop the context depending on the control being used.
- match node.value.value.as_str() {
- ".macro" => {
+ match node.node_type {
+ NodeType::Control(ControlType::StartMacro) => {
self.context_push(&node.left.clone().unwrap());
Ok((true, false))
}
- ".proc" | ".scope" => {
+ NodeType::Control(ControlType::StartProc)
+ | NodeType::Control(ControlType::StartScope) => {
self.context_push(&node.left.clone().unwrap());
Ok((true, true))
}
- ".endmacro" | ".endproc" | ".endscope" => {
+ NodeType::Control(ControlType::EndMacro)
+ | NodeType::Control(ControlType::EndProc)
+ | NodeType::Control(ControlType::EndScope) => {
self.context_pop(&node.value)?;
Ok((true, true))
}
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<usize>,
pub touches_context: bool,
@@ -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
};
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<PNode, ParseError> {
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());