use std::fs; use std::path::PathBuf; use std::{collections::HashMap, fs::File}; use std::io::BufReader; use bincode::Error; use ropey::Rope; use serde::{Serialize, Deserialize}; use sv_parser::{unwrap_node, RefNode}; use xml::reader::{EventReader, XmlEvent}; use super::hdlparam::{FastHdlparam, InstParameter, Macro, Range}; use super::sv_parser::{get_identifier, get_instance_params, get_instance_ports, get_position, get_pp_range}; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Template { pub text: String, pub fast: FastHdlparam } #[derive(Debug, Serialize, Deserialize, Clone, Default)] pub struct PrimitiveXml { pub name_to_template: HashMap } pub fn init_parse_primitive_files() -> Result { const XML_DIR: &str = "../../primitive_files"; let mut primitive_xml = PrimitiveXml::default(); for entry in fs::read_dir(XML_DIR)? { let entry = entry?; let path = entry.path(); if let Some(ext) = path.extension() { if ext == "xml" { if let Ok(primitive) = xml_parser(path.to_str().unwrap()) { primitive_xml.name_to_template.extend( primitive.name_to_template ); }; } } } Ok(primitive_xml) } fn xml_parser(path: &str) -> Result { let file = File::open(path)?; let file = BufReader::new(file); let mut parser = EventReader::new(file); let mut primitive_xml = PrimitiveXml::default(); loop { match parser.next() { Ok(XmlEvent::StartElement { name, .. }) => { if name.local_name == "Template" { let (inst_name , template) = xml_parse_template(&mut parser); primitive_xml.name_to_template.insert(inst_name, template); } } Ok(XmlEvent::EndElement { name }) => { if name.local_name == "RootFolder" { break; } } _ => () } } Ok((primitive_xml)) } fn xml_parse_template(parser: &mut EventReader>) -> (String, Template) { loop { match parser.next() { Ok(XmlEvent::Characters(text)) => { if text.contains("Cut code below this line") { if let Some((name, text, fast)) = xml_parse_text(&text) { return (name, Template { text, fast }); } } } _ => () } } } fn xml_parse_text(text: &str) -> Option<(String, String, FastHdlparam)> { let module_string = "module primitive_module();\n".to_string() + text + "\nendmodule"; if let Ok((syntax_tree, _)) = sv_parser::parse_sv_str( &module_string, PathBuf::new(), &HashMap::new(), &Vec::::new(), true, true ) { let doc = Rope::from_str(syntax_tree.text.text()); let mut res_inst_name = String::new(); let mut hdlparam = FastHdlparam { fast_macro: Macro { defines: Vec::new(), errors: Vec::new(), includes: Vec::new(), invalid: Vec::new() }, content: Vec::new(), file_type: "primitive".to_string() }; let res_text = syntax_tree.text.text().to_string(); for node in &syntax_tree { match node { RefNode::ModuleDeclaration(x) => { let start_keyword = unwrap_node!(x, Keyword).unwrap(); let start_keyword = get_identifier(start_keyword).unwrap(); let start_pos = get_position(&doc, start_keyword, 0); let module_range = get_pp_range(&doc, RefNode::ModuleDeclaration(x)); let module_range = Range { start: start_pos, end: module_range.end }; let id = unwrap_node!(x, ModuleIdentifier).unwrap(); let id = get_identifier(id).unwrap(); let name = syntax_tree.get_str(&id).unwrap(); hdlparam.new_module(name, module_range); } RefNode::ModuleInstantiation(x) => { if let Some(id) = unwrap_node!(x, ModuleIdentifier) { let id = get_identifier(id).unwrap(); let inst_type = syntax_tree.get_str(&id).unwrap(); let start_pos = get_position(&doc, id, 0); let range = get_pp_range(&doc, RefNode::ModuleInstantiation(x)); let inst_range = Range { start: start_pos, end: range.end }; if let Some(id) = unwrap_node!(x, HierarchicalInstance) { let hier_node = id.clone(); let id = get_identifier(id).unwrap(); let name = syntax_tree.get_str(&id).unwrap(); let param_range = match unwrap_node!(x, ParameterValueAssignment) { Some(inst_node) => { get_pp_range(&doc, inst_node).to_option() } _ => None }; let inst_param_assignments = if let Some(param_node) = unwrap_node!(x, ParameterValueAssignment) { get_instance_params(&syntax_tree, &doc, param_node) } else { Vec::::new() }; let inst_port_assignments = get_instance_ports(&syntax_tree, &doc, hier_node.clone()); let port_range = get_pp_range(&doc, hier_node).to_option(); res_inst_name = inst_type.to_string(); hdlparam.add_instance( name, inst_type, inst_range, param_range, inst_param_assignments, port_range, inst_port_assignments ); } } } RefNode::GateInstantiation(x) => { let id = unwrap_node!(x, GateInstantiation).unwrap(); let id = get_identifier(id).unwrap(); let inst_type = syntax_tree.get_str(&id).unwrap(); let start_pos = get_position(&doc, id, 0); let range = get_pp_range(&doc, RefNode::GateInstantiation(x)); let inst_range = Range { start: start_pos, end: range.end }; match unwrap_node!(x, NInputGateInstance, NOutputGateInstance) { Some(id) => { let gate_node = id.clone(); let id = get_identifier(id).unwrap(); let name = syntax_tree.get_str(&id).unwrap(); let param_range = None; let inst_port_assignments = get_instance_ports(&syntax_tree, &doc, gate_node.clone()); let port_range = get_pp_range(&doc, gate_node).to_option(); hdlparam.add_instance( name, inst_type, inst_range, param_range, Vec::::new(), port_range, inst_port_assignments ); } _ => () } } _ => () } } Some((res_inst_name, res_text, hdlparam)) } else { None } } #[cfg(test)] mod tests { use std::{fs, path::Path}; use super::xml_parser; const TESTFILE: &str = "D:\\work\\playground\\digital-lsp-server\\primitive_files\\verilog1.xml"; const TESTDIR: &str = "/home/dide/project/Digital-Test/Digital-IDE-test/user/factory/xilinx"; #[test] fn test_xml() { let _ = xml_parser(TESTFILE); } #[test] fn test_dir() { // 判断路径是否存在且为文件夹 let path = Path::new(TESTDIR); if path.exists() && path.is_dir() { // 递归遍历文件夹 if let Err(e) = traverse_directory(path) { eprintln!("Error: {}", e); } } else { eprintln!("Path does not exist or is not a directory"); } } fn traverse_directory(dir: &Path) -> Result<(), Box> { if dir.is_dir() { for entry in fs::read_dir(dir)? { let entry = entry?; let path = entry.path(); if path.is_dir() { // 递归遍历子文件夹 traverse_directory(&path)?; } else if path.is_file() { // 检查文件扩展名 if let Some(ext) = path.extension() { if ext == "xml" { println!("Test file: {:?}", path); let file_path = path.to_str().unwrap(); let _ = xml_parser(file_path); } } } } } Ok(()) } }