diff --git a/directives/gas_directives.xml b/directives/gas_directives.xml
new file mode 100644
index 00000000..bfda4f77
--- /dev/null
+++ b/directives/gas_directives.xml
@@ -0,0 +1,852 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/registers/x86_64.xml b/registers/x86_64.xml
index 5f17e35f..fb7e73c4 100644
--- a/registers/x86_64.xml
+++ b/registers/x86_64.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/src/bin/main.rs b/src/bin/main.rs
index 2cc1c33a..d80397c6 100644
--- a/src/bin/main.rs
+++ b/src/bin/main.rs
@@ -39,7 +39,7 @@ pub fn main() -> anyhow::Result<()> {
completion_item: Some(CompletionOptionsCompletionItem {
label_details_support: Some(true),
}),
- trigger_characters: Some(vec![String::from("%")]),
+ trigger_characters: Some(vec![String::from("%"), String::from(".")]),
..Default::default()
});
@@ -84,10 +84,6 @@ pub fn main() -> anyhow::Result<()> {
let xml_conts_x86 = include_str!("../../opcodes/x86.xml");
populate_instructions(xml_conts_x86)?
.into_iter()
- .map(|mut instruction| {
- instruction.arch = Some(Arch::X86);
- instruction
- })
.map(|instruction| {
// filter out assemblers by user config
instr_filter_targets(&instruction, &target_config)
@@ -103,10 +99,6 @@ pub fn main() -> anyhow::Result<()> {
let xml_conts_x86_64 = include_str!("../../opcodes/x86_64.xml");
populate_instructions(xml_conts_x86_64)?
.into_iter()
- .map(|mut instruction| {
- instruction.arch = Some(Arch::X86_64);
- instruction
- })
.map(|instruction| {
// filter out assemblers by user config
instr_filter_targets(&instruction, &target_config)
@@ -133,10 +125,6 @@ pub fn main() -> anyhow::Result<()> {
let xml_conts_regs_x86 = include_str!("../../registers/x86.xml");
populate_registers(xml_conts_regs_x86)?
.into_iter()
- .map(|mut reg| {
- reg.arch = Some(Arch::X86);
- reg
- })
.collect()
} else {
Vec::new()
@@ -147,10 +135,6 @@ pub fn main() -> anyhow::Result<()> {
let xml_conts_regs_x86_64 = include_str!("../../registers/x86_64.xml");
populate_registers(xml_conts_regs_x86_64)?
.into_iter()
- .map(|mut reg| {
- reg.arch = Some(Arch::X86_64);
- reg
- })
.collect()
} else {
Vec::new()
@@ -160,10 +144,23 @@ pub fn main() -> anyhow::Result<()> {
populate_name_to_register_map(Arch::X86, &x86_registers, &mut names_to_registers);
populate_name_to_register_map(Arch::X86_64, &x86_64_registers, &mut names_to_registers);
+ let gas_directives = if target_config.assemblers.gas {
+ info!("Populating directive set -> Gas...");
+ let xml_conts_gas = include_str!("../../directives/gas_directives.xml");
+ populate_directives(xml_conts_gas)?.into_iter().collect()
+ } else {
+ Vec::new()
+ };
+
+ let mut names_to_directives = NameToDirectiveMap::new();
+ populate_name_to_directive_map(Assembler::Gas, &gas_directives, &mut names_to_directives);
+
let instr_completion_items =
get_completes(&names_to_instructions, Some(CompletionItemKind::OPERATOR));
let reg_completion_items =
get_completes(&names_to_registers, Some(CompletionItemKind::VARIABLE));
+ let directive_completion_items =
+ get_completes(&names_to_directives, Some(CompletionItemKind::OPERATOR));
let include_dirs = get_include_dirs();
@@ -171,8 +168,10 @@ pub fn main() -> anyhow::Result<()> {
&connection,
initialization_params,
&names_to_instructions,
+ &names_to_directives,
&names_to_registers,
&instr_completion_items,
+ &directive_completion_items,
®_completion_items,
&include_dirs,
)?;
@@ -183,12 +182,15 @@ pub fn main() -> anyhow::Result<()> {
Ok(())
}
+#[allow(clippy::too_many_arguments)]
fn main_loop(
connection: &Connection,
params: serde_json::Value,
names_to_instructions: &NameToInstructionMap,
+ names_to_directives: &NameToDirectiveMap,
names_to_registers: &NameToRegisterMap,
instruction_completion_items: &[CompletionItem],
+ directive_completion_items: &[CompletionItem],
register_completion_items: &[CompletionItem],
include_dirs: &[PathBuf],
) -> anyhow::Result<()> {
@@ -219,13 +221,13 @@ fn main_loop(
get_word_from_pos_params(
doc,
¶ms.text_document_position_params,
- ".",
+ "",
),
// treat the word under the cursor as a filename and grab it as well
get_word_from_pos_params(
doc,
¶ms.text_document_position_params,
- "",
+ ".",
),
)
} else {
@@ -239,6 +241,7 @@ fn main_loop(
file_word,
names_to_instructions,
names_to_registers,
+ names_to_directives,
include_dirs,
);
match hover_res {
@@ -271,6 +274,7 @@ fn main_loop(
&mut tree,
¶ms,
instruction_completion_items,
+ directive_completion_items,
register_completion_items,
);
match comp_res {
diff --git a/src/lib.rs b/src/lib.rs
index c5506bd6..ce9d43e3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,6 +5,6 @@ pub mod x86_parser;
pub use lsp::*;
pub use types::*;
pub use x86_parser::{
- populate_instructions, populate_name_to_instruction_map, populate_name_to_register_map,
- populate_registers,
+ populate_directives, populate_instructions, populate_name_to_directive_map,
+ populate_name_to_instruction_map, populate_name_to_register_map, populate_registers,
};
diff --git a/src/lsp.rs b/src/lsp.rs
index b175e4bd..80cbaf5b 100644
--- a/src/lsp.rs
+++ b/src/lsp.rs
@@ -1,5 +1,8 @@
use crate::types::Column;
-use crate::{Arch, Completable, Hoverable, Instruction, NameToInstructionMap, TargetConfig};
+use crate::{
+ Arch, ArchOrAssembler, Assembler, Completable, Hoverable, Instruction, NameToInstructionMap,
+ TargetConfig,
+};
use dirs::config_dir;
use log::{error, info, log, log_enabled};
use lsp_textdocument::FullTextDocument;
@@ -192,12 +195,12 @@ pub fn text_doc_change_to_ts_edit(
/// Given a NameTo_SomeItem_ map, returns a `Vec` for the items
/// contained within the map
-pub fn get_completes(
- map: &HashMap<(Arch, &str), T>,
+pub fn get_completes(
+ map: &HashMap<(U, &str), T>,
kind: Option,
) -> Vec {
map.iter()
- .map(|((_arch, name), item_info)| {
+ .map(|((_arch_or_asm, name), item_info)| {
let value = format!("{}", item_info);
CompletionItem {
@@ -213,25 +216,30 @@ pub fn get_completes(
.collect()
}
-pub fn get_hover_resp(
+pub fn get_hover_resp(
word: &str,
file_word: &str,
instruction_map: &HashMap<(Arch, &str), T>,
- register_map: &HashMap<(Arch, &str), S>,
+ register_map: &HashMap<(Arch, &str), U>,
+ directive_map: &HashMap<(Assembler, &str), V>,
include_dirs: &[PathBuf],
) -> Option {
- let instr_lookup = lookup_hover_resp(word, instruction_map);
+ let instr_lookup = lookup_hover_resp_by_arch(word, instruction_map);
if instr_lookup.is_some() {
return instr_lookup;
}
- let reg_lookup = lookup_hover_resp(word, register_map);
+ let directive_lookup = lookup_hover_resp_by_assembler(word, directive_map);
+ if directive_lookup.is_some() {
+ return directive_lookup;
+ }
+
+ let reg_lookup = lookup_hover_resp_by_arch(word, register_map);
if reg_lookup.is_some() {
return reg_lookup;
}
let demang = get_demangle_resp(word);
-
if demang.is_some() {
return demang;
}
@@ -244,8 +252,11 @@ pub fn get_hover_resp(
None
}
-fn lookup_hover_resp(word: &str, map: &HashMap<(Arch, &str), T>) -> Option {
- let (x86_res, x86_64_res) = search_for_hoverable(word, map);
+fn lookup_hover_resp_by_arch(
+ word: &str,
+ map: &HashMap<(Arch, &str), T>,
+) -> Option {
+ let (x86_res, x86_64_res) = search_for_hoverable_by_arch(word, map);
match (x86_res.is_some(), x86_64_res.is_some()) {
(true, _) | (_, true) => {
@@ -275,6 +286,36 @@ fn lookup_hover_resp(word: &str, map: &HashMap<(Arch, &str), T>) -
}
}
+fn lookup_hover_resp_by_assembler(
+ word: &str,
+ map: &HashMap<(Assembler, &str), T>,
+) -> Option {
+ let (gas_res, go_res) = search_for_hoverable_by_assembler(word, map);
+
+ match (gas_res.is_some(), go_res.is_some()) {
+ (true, _) | (_, true) => {
+ let mut value = String::new();
+ if let Some(gas_res) = gas_res {
+ value += &format!("{}", gas_res);
+ }
+ if let Some(go_res) = go_res {
+ value += &format!("{}{}", if gas_res.is_some() { "\n\n" } else { "" }, go_res);
+ }
+ Some(Hover {
+ contents: HoverContents::Markup(MarkupContent {
+ kind: MarkupKind::Markdown,
+ value,
+ }),
+ range: None,
+ })
+ }
+ _ => {
+ // don't know of this word
+ None
+ }
+ }
+}
+
fn get_demangle_resp(word: &str) -> Option {
let name = Name::new(word, NameMangling::Mangled, Language::Unknown);
let demangled = name.demangle(DemangleOptions::complete());
@@ -368,18 +409,31 @@ pub fn get_comp_resp(
curr_tree: &mut Option,
params: &CompletionParams,
instr_comps: &[CompletionItem],
+ dir_comps: &[CompletionItem],
reg_comps: &[CompletionItem],
) -> Option {
let cursor_line = params.text_document_position.position.line as usize;
let cursor_char = params.text_document_position.position.character as usize;
- // prepend register names with "%" in GAS
if let Some(ctx) = params.context.as_ref() {
if ctx.trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
- return Some(CompletionList {
- is_incomplete: true,
- items: filtered_comp_list(reg_comps),
- });
+ match ctx.trigger_character.as_ref().map(|s| s.as_ref()) {
+ // prepend GAS registers with "%"
+ Some("%") => {
+ return Some(CompletionList {
+ is_incomplete: true,
+ items: filtered_comp_list(reg_comps),
+ });
+ }
+ // prepend GAS directives with "."
+ Some(".") => {
+ return Some(CompletionList {
+ is_incomplete: true,
+ items: filtered_comp_list(dir_comps),
+ });
+ }
+ _ => {}
+ }
}
}
@@ -399,6 +453,30 @@ pub fn get_comp_resp(
});
let curr_doc = curr_doc.as_bytes();
+ static QUERY_DIRECTIVE: Lazy = Lazy::new(|| {
+ tree_sitter::Query::new(
+ tree_sitter_asm::language(),
+ "(meta kind: (meta_ident) @directive)",
+ )
+ .unwrap()
+ });
+ let matches_iter = cursor.matches(&QUERY_DIRECTIVE, tree.root_node(), curr_doc);
+
+ for match_ in matches_iter {
+ let caps = match_.captures;
+ for cap in caps.iter() {
+ let arg_start = cap.node.range().start_point;
+ let arg_end = cap.node.range().end_point;
+ if cursor_matches!(cursor_line, cursor_char, arg_start, arg_end) {
+ let items = filtered_comp_list(dir_comps);
+ return Some(CompletionList {
+ is_incomplete: true,
+ items,
+ });
+ }
+ }
+ }
+
static QUERY_INSTR_ANY: Lazy = Lazy::new(|| {
tree_sitter::Query::new(
tree_sitter_asm::language(),
@@ -598,7 +676,8 @@ pub fn get_sig_help_resp(
let mut value = String::new();
let mut has_x86 = false;
let mut has_x86_64 = false;
- let (x86_info, x86_64_info) = search_for_hoverable(instr_name, instr_info);
+ let (x86_info, x86_64_info) =
+ search_for_hoverable_by_arch(instr_name, instr_info);
if let Some(sig) = x86_info {
for form in sig.forms.iter() {
if let Some(ref gas_name) = form.gas_name {
@@ -804,7 +883,7 @@ pub fn get_ref_resp(
// If issue is resolved, can add a separate lifetime "'b" to "word"
// parameter such that 'a: 'b
// For now, using 'a for both isn't strictly necessary, but fits our use case
-fn search_for_hoverable<'a, T: Hoverable>(
+fn search_for_hoverable_by_arch<'a, T: Hoverable>(
word: &'a str,
map: &'a HashMap<(Arch, &str), T>,
) -> (Option<&'a T>, Option<&'a T>) {
@@ -814,6 +893,16 @@ fn search_for_hoverable<'a, T: Hoverable>(
(x86_res, x86_64_res)
}
+fn search_for_hoverable_by_assembler<'a, T: Hoverable>(
+ word: &'a str,
+ map: &'a HashMap<(Assembler, &str), T>,
+) -> (Option<&'a T>, Option<&'a T>) {
+ let gas_res = map.get(&(Assembler::Gas, word));
+ let go_res = map.get(&(Assembler::Go, word));
+
+ (gas_res, go_res)
+}
+
/// Searches for global config in ~/.config/asm-lsp, then the project's directory
/// Project specific configs will override global configs
pub fn get_target_config(params: &InitializeParams) -> TargetConfig {
diff --git a/src/types.rs b/src/types.rs
index 9603d3fb..20c1c2a8 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -167,6 +167,101 @@ impl std::fmt::Display for InstructionForm {
}
}
+// Directive ------------------------------------------------------------------------------------
+#[derive(Debug, Clone)]
+pub struct Directive {
+ pub name: String,
+ pub alt_names: Vec,
+ pub signatures: Vec,
+ pub description: String,
+ pub deprecated: bool,
+ pub url: Option,
+ pub assembler: Option,
+}
+
+impl Hoverable for &Directive {}
+impl Completable for &Directive {}
+
+impl Default for Directive {
+ fn default() -> Self {
+ let name = String::new();
+ let alt_names = vec![];
+ let signatures = vec![];
+ let description = String::new();
+ let deprecated = false;
+ let url = None;
+ let assembler = None;
+
+ Self {
+ name,
+ alt_names,
+ signatures,
+ description,
+ deprecated,
+ url,
+ assembler,
+ }
+ }
+}
+
+impl std::fmt::Display for Directive {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ // basic fields
+ let header: String;
+ if let Some(assembler) = &self.assembler {
+ header = format!(
+ ".{} [{}]{}",
+ &self.name,
+ assembler.as_ref(),
+ if self.deprecated {
+ "\n**DEPRECATED**"
+ } else {
+ ""
+ }
+ );
+ } else {
+ header = self.name.clone();
+ }
+
+ let mut v: Vec<&str> = vec![&header, &self.description, "\n"];
+
+ // signature(s)
+ let mut sigs = String::new();
+ for sig in self.signatures.iter() {
+ sigs += &format!("- {}\n", sig);
+ }
+ v.push(&sigs);
+
+ // url
+ let more_info: String;
+ match &self.url {
+ None => {}
+ Some(url_) => {
+ more_info = format!("\nMore info: {}", url_);
+ v.push(&more_info);
+ }
+ }
+
+ let s = v.join("\n");
+ write!(f, "{}", s)?;
+ Ok(())
+ }
+}
+
+impl<'own> Directive {
+ /// get the names of all the associated directives
+ pub fn get_associated_names(&'own self) -> Vec<&'own str> {
+ let mut names = Vec::<&'own str>::new();
+ names.push(&self.name);
+
+ for name in &self.alt_names {
+ names.push(name);
+ }
+
+ names
+ }
+}
+
// Register ---------------------------------------------------------------------------------------
#[derive(Debug, Clone)]
pub struct Register {
@@ -287,10 +382,15 @@ pub type NameToInstructionMap<'instruction> =
pub type NameToRegisterMap<'register> = HashMap<(Arch, &'register str), &'register Register>;
+pub type NameToDirectiveMap<'directive> =
+ HashMap<(Assembler, &'directive str), &'directive Directive>;
+
// Define a trait for types we display on Hover Requests so we can avoid some duplicate code
pub trait Hoverable: Display + Clone + Copy {}
// Define a trait for types we display on Completion Requests so we can avoid some duplicate code
pub trait Completable: Display {}
+// Define a trait for the enums we use to distinguish between different Architectures and Assemblers
+pub trait ArchOrAssembler {}
#[derive(Debug, Clone, EnumString, AsRefStr)]
pub enum XMMMode {
@@ -304,12 +404,24 @@ pub enum MMXMode {
MMX,
}
-#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, EnumString, AsRefStr)]
+#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, Display, EnumString, AsRefStr)]
pub enum Arch {
+ #[strum(serialize = "x86")]
X86,
+ #[strum(serialize = "x86-64")]
X86_64,
}
+impl ArchOrAssembler for Arch {}
+
+#[derive(Debug, Display, Hash, PartialEq, Eq, Clone, Copy, EnumString, AsRefStr)]
+pub enum Assembler {
+ Gas,
+ Go,
+}
+
+impl ArchOrAssembler for Assembler {}
+
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, EnumString, AsRefStr, Display)]
pub enum RegisterType {
#[strum(serialize = "General Purpose Register")]
diff --git a/src/x86_parser.rs b/src/x86_parser.rs
index 7cd0bafd..4ee10050 100644
--- a/src/x86_parser.rs
+++ b/src/x86_parser.rs
@@ -2,6 +2,7 @@ use crate::types::*;
use anyhow::anyhow;
use log::{debug, error, info, warn};
+use quick_xml::escape::unescape;
use quick_xml::events::attributes::Attribute;
use quick_xml::events::Event;
use quick_xml::name::QName;
@@ -32,6 +33,7 @@ pub fn populate_instructions(xml_contents: &str) -> anyhow::Result = None;
debug!("Parsing XML contents...");
loop {
@@ -39,9 +41,19 @@ pub fn populate_instructions(xml_contents: &str) -> anyhow::Result {
match e.name() {
+ QName(b"InstructionSet") => {
+ for attr in e.attributes() {
+ let Attribute { key, value } = attr.unwrap();
+ if let Ok("name") = str::from_utf8(key.into_inner()) {
+ arch = Arch::from_str(unsafe { str::from_utf8_unchecked(&value) })
+ .ok();
+ }
+ }
+ }
QName(b"Instruction") => {
// start of a new instruction
curr_instruction = Instruction::default();
+ curr_instruction.arch = arch;
// iterate over the attributes
for attr in e.attributes() {
@@ -323,6 +335,7 @@ pub fn populate_registers(xml_contents: &str) -> anyhow::Result> {
// ref to the register that's currently under construction
let mut curr_register = Register::default();
let mut curr_bit_flag = RegisterBitInfo::default();
+ let mut arch: Option = None;
debug!("Parsing XML contents...");
loop {
@@ -330,9 +343,19 @@ pub fn populate_registers(xml_contents: &str) -> anyhow::Result> {
// start event ------------------------------------------------------------------------
Ok(Event::Start(ref e)) => {
match e.name() {
+ QName(b"InstructionSet") => {
+ for attr in e.attributes() {
+ let Attribute { key, value } = attr.unwrap();
+ if let Ok("name") = str::from_utf8(key.into_inner()) {
+ arch = Arch::from_str(unsafe { str::from_utf8_unchecked(&value) })
+ .ok();
+ }
+ }
+ }
QName(b"Register") => {
// start of a new register
curr_register = Register::default();
+ curr_register.arch = arch;
// iterate over the attributes
for attr in e.attributes() {
@@ -412,7 +435,7 @@ pub fn populate_registers(xml_contents: &str) -> anyhow::Result> {
Ok(Event::End(ref e)) => {
match e.name() {
QName(b"Register") => {
- // finish instruction
+ // finish register
registers_map.insert(curr_register.name.clone(), curr_register.clone());
}
QName(b"Flag") => {
@@ -446,6 +469,114 @@ pub fn populate_name_to_register_map<'register>(
}
}
+pub fn populate_directives(xml_contents: &str) -> anyhow::Result> {
+ let mut directives_map = HashMap::::new();
+
+ // iterate through the XML --------------------------------------------------------------------
+ let mut reader = Reader::from_str(xml_contents);
+ reader.trim_text(true);
+
+ // ref to the assembler directive that's currently under construction
+ let mut curr_directive = Directive::default();
+ let mut assembler: Option = None;
+
+ debug!("Parsing XML contents...");
+ loop {
+ match reader.read_event() {
+ // start event ------------------------------------------------------------------------
+ Ok(Event::Start(ref e)) => {
+ match e.name() {
+ QName(b"Assembler") => {
+ for attr in e.attributes() {
+ let Attribute { key, value } = attr.unwrap();
+ if let Ok("name") = str::from_utf8(key.into_inner()) {
+ assembler = Assembler::from_str(unsafe {
+ str::from_utf8_unchecked(&value)
+ })
+ .ok();
+ }
+ }
+ }
+ QName(b"Directive") => {
+ // start of a new directive
+ curr_directive = Directive::default();
+ curr_directive.assembler = assembler;
+
+ // iterate over the attributes
+ for attr in e.attributes() {
+ let Attribute { key, value } = attr.unwrap();
+ match str::from_utf8(key.into_inner()).unwrap() {
+ "name" => {
+ let name =
+ String::from(unsafe { str::from_utf8_unchecked(&value) });
+ curr_directive.alt_names.push(name.to_uppercase());
+ curr_directive.name = name;
+ }
+ "md_description" => {
+ let description =
+ String::from(unsafe { str::from_utf8_unchecked(&value) });
+ curr_directive.description =
+ unescape(&description).unwrap().to_string();
+ }
+ "deprecated" => {
+ curr_directive.deprecated = FromStr::from_str(unsafe {
+ str::from_utf8_unchecked(&value)
+ })
+ .unwrap();
+ }
+ "url_fragment" => {
+ curr_directive.url = Some(format!(
+ "https://sourceware.org/binutils/docs-2.41/as/{}.html",
+ unsafe { str::from_utf8_unchecked(&value) }
+ ));
+ }
+ _ => {}
+ }
+ }
+ }
+ QName(b"Signatures") => {} // it's just a wrapper...
+ QName(b"Signature") => {
+ for attr in e.attributes() {
+ let Attribute { key, value } = attr.unwrap();
+ if let Ok("sig") = str::from_utf8(key.into_inner()) {
+ let sig = String::from(unsafe { str::from_utf8_unchecked(&value) });
+ curr_directive
+ .signatures
+ .push(unescape(&sig).unwrap().to_string());
+ }
+ }
+ }
+ _ => {} // unknown event
+ }
+ }
+ // end event --------------------------------------------------------------------------
+ Ok(Event::End(ref e)) => {
+ if let QName(b"Directive") = e.name() {
+ // finish directive
+ directives_map.insert(curr_directive.name.clone(), curr_directive.clone());
+ }
+ }
+ Ok(Event::Eof) => break,
+ Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
+ _ => (), // rest of events that we don't consider
+ }
+ }
+
+ Ok(directives_map.into_values().collect())
+}
+
+pub fn populate_name_to_directive_map<'directive>(
+ assem: Assembler,
+ directives: &'directive Vec,
+ names_to_directives: &mut NameToDirectiveMap<'directive>,
+) {
+ for register in directives {
+ for name in ®ister.get_associated_names() {
+ names_to_directives.insert((assem, name), register);
+ }
+ }
+}
+
fn get_docs_body(x86_online_docs: &str) -> Option {
// provide a URL example page -----------------------------------------------------------------
// 1. If the cache refresh option is enabled or the cache doesn't exist, attempt to fetch the