252 lines
9.6 KiB
Rust
252 lines
9.6 KiB
Rust
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<String, Template>
|
|
}
|
|
|
|
pub fn init_parse_primitive_files() -> Result<PrimitiveXml, Error> {
|
|
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<PrimitiveXml, Error> {
|
|
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<BufReader<File>>) -> (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::<PathBuf>::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::<InstParameter>::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::<InstParameter>::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<dyn std::error::Error>> {
|
|
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(())
|
|
}
|
|
} |