diff options
| -rw-r--r-- | Cargo.lock | 40 | ||||
| -rw-r--r-- | Cargo.toml | 4 | ||||
| -rw-r--r-- | crates/nasm/Cargo.toml | 4 | ||||
| -rw-r--r-- | lib/xixanta/src/assembler.rs | 94 | ||||
| -rw-r--r-- | lib/xixanta/src/context.rs | 13 | ||||
| -rw-r--r-- | lib/xixanta/src/node.rs | 30 | ||||
| -rw-r--r-- | lib/xixanta/src/opcodes.rs | 30 | ||||
| -rw-r--r-- | lib/xixanta/src/parser.rs | 70 |
8 files changed, 182 insertions, 103 deletions
@@ -4,9 +4,9 @@ version = 3 [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" dependencies = [ "anstyle", "anstyle-parse", @@ -19,33 +19,33 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", "windows-sys", @@ -53,9 +53,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.89" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" [[package]] name = "clap" @@ -99,9 +99,9 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "heck" @@ -146,9 +146,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.87" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] @@ -170,9 +170,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.79" +version = "2.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" dependencies = [ "proc-macro2", "quote", @@ -193,9 +193,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "windows-sys" -version = "0.52.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] @@ -10,3 +10,7 @@ authors = ["Miquel Sabaté Solà"] [workspace.dependencies] xixanta = { path = "./lib/xixanta", version = "0.1.0" } + +[profile.release] +codegen-units = 1 +lto = true diff --git a/crates/nasm/Cargo.toml b/crates/nasm/Cargo.toml index b89b48c..110e5ad 100644 --- a/crates/nasm/Cargo.toml +++ b/crates/nasm/Cargo.toml @@ -5,6 +5,6 @@ edition = "2021" authors = ["Miquel Sabaté Solà <mikisabate@gmail.com>"] [dependencies] -anyhow = "1.0.82" -clap = { version = "4.5.4", features = ["derive"] } +anyhow = "^1" +clap = { version = "^4", features = ["derive"] } xixanta.workspace = true 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()); |
