use std::fs::{self, File}; use std::io::BufRead; use std::io::BufReader; use std::path::PathBuf; use regex::Regex; use ropey::Rope; use tower_lsp::lsp_types::Url; use sv_parser::{unwrap_node, Locate, RefNode, SyntaxTree}; use crate::sources::recovery_sv_parse; use crate::utils::to_escape_path; use super::hdlparam::{FastHdlparam, Macro}; #[allow(unused)] pub fn sv_parser(path: &str) -> Option { // The path of SystemVerilog source file let path = PathBuf::from(path); // The list of include paths let includes: Vec = Vec::new(); let text = match fs::read_to_string(&path) { Ok(text) => text, Err(_) => return None }; let doc = Rope::from_str(&text); let uri = Url::from_file_path(&path).unwrap(); let result = recovery_sv_parse(&doc, &uri, &None, &includes); // println!("result: {result:?}"); if let Some(syntax_tree) = result { if let Ok(hdlparam) = make_fast_from_syntaxtree(&syntax_tree, &path) { return Some(hdlparam); } } None } pub fn make_fast_from_syntaxtree(syntax_tree: &SyntaxTree, path: &PathBuf) -> Result { // 对不同操作系统文件路径的支持 let path = to_escape_path(path); let mut hdlparam = FastHdlparam { fast_macro: Macro { defines: Vec::new(), errors: Vec::new(), includes: get_includes(&path), invalid: Vec::new() }, content: Vec::new() }; let mut ansi_port_last_dir = ""; let content = syntax_tree.text.text().split('\n') .map(|s| s.to_string()) .collect::>(); // println!("{:?}", syntax_tree); // &SyntaxTree is iterable for node in syntax_tree { match node { RefNode::TextMacroDefinition(x) => { if let Some(start) = unwrap_node!(x, TextMacroDefinition) { let start = get_identifier(start).unwrap(); let (start_line, start_character) = (start.line, get_column_by_offset(&content, start.offset)); let name = if let Some(name) = unwrap_node!(x, TextMacroName) { let name = get_identifier(name).unwrap(); syntax_tree.get_str(&name).unwrap() } else { "unknown" }; let mut params_vec = Vec::new(); if let Some(RefNode::ListOfFormalArguments(x)) = unwrap_node!(x, ListOfFormalArguments) { for node in x { if let RefNode::FormalArgument(x) = node { let param_name = if let Some(param_name) = unwrap_node!(x, SimpleIdentifier) { let param_name = get_identifier(param_name).unwrap(); syntax_tree.get_str(¶m_name).unwrap() } else { "unknown" }; let param_val = match unwrap_node!(x, DefaultText) { Some(RefNode::DefaultText(x)) => syntax_tree.get_str(&x.nodes.0).unwrap(), _ => "Unknown" }; params_vec.push(crate::core::hdlparam::DefineParam { name: param_name.to_string(), value: param_val.to_string() }); } } } let (end_line, end_character, replacement) = if let Some(RefNode::MacroText(x)) = unwrap_node!(x, MacroText) { let replacement = x.nodes.0; (replacement.line, get_column_by_offset(&content, replacement.offset) + replacement.len, syntax_tree.get_str(&replacement).unwrap()) } else { (start_line, start_character + start.len, "unknown") }; hdlparam.add_define(name, replacement, start_line, start_character as u32, end_line, end_character as u32, params_vec); } } RefNode::ModuleDeclaration(x) => { let id = unwrap_node!(x, ModuleIdentifier).unwrap(); let id = get_identifier(id).unwrap(); let (line, character) = (id.line, get_column_by_offset(&content, id.offset) as u32); let end_character = character + id.len as u32; let name = syntax_tree.get_str(&id).unwrap(); hdlparam.new_module(name, line, character, end_character); } RefNode::ParameterPortDeclaration(x) => { if let Some(id) = unwrap_node!(x, ParameterIdentifier) { let id = get_identifier(id).unwrap(); let name = syntax_tree.get_str(&id).unwrap(); match unwrap_node!(x, ParameterDeclarationParam, ParameterPortDeclarationParamList) { Some(RefNode::ParameterDeclarationParam(param_node)) => { // println!("{:?}", param_node); let keyword_locate = param_node.nodes.0.nodes.0; // println!("keyword {:#?}", keyword_locate); let (start_line, start_character) = (id.line, get_column_by_offset(&content, keyword_locate.offset) as u32); let (mut end_line, mut end_character) = (id.line, start_character + id.len as u32); let net_type = match unwrap_node!(param_node, DataType) { Some(RefNode::DataType(data_type)) => { let id = unwrap_node!(data_type, SimpleIdentifier, Keyword); if id == None { "wire" } else { let id = get_identifier(id.unwrap()).unwrap(); syntax_tree.get_str(&id).unwrap() } } _ => "wire" }; let init = if let Some(init) = unwrap_node!(param_node, ConstantMintypmaxExpression) { match init { RefNode::ConstantMintypmaxExpression(expression) => { // println!("expression {:?}", x); let param_init = sv_parser::NeedParseExpression::Parameter(expression.clone()); let (exp, last_locate) = parse_expression(&syntax_tree, ¶m_init); (end_line, end_character) = if last_locate != None { // println!("param {:?} lastlocate {:?}", name, last_locate); (last_locate.unwrap().line, (get_column_by_offset(&content, last_locate.unwrap().offset) + last_locate.unwrap().len) as u32) } else { (end_line, end_character) }; // println!("end pos {} {}", end_line, end_character); exp } _ => "unknown".to_string() } } else { "unknown".to_string() }; hdlparam.add_parameter(name, net_type, init.as_str(), start_line, start_character, end_line, end_character); } _ => () } } } RefNode::ParameterDeclaration(x) => { if let Some(id) = unwrap_node!(x, ParameterIdentifier) { let id = get_identifier(id).unwrap(); let name = syntax_tree.get_str(&id).unwrap(); match unwrap_node!(x, ParameterDeclarationParam, ParameterPortDeclarationParamList) { Some(RefNode::ParameterDeclarationParam(param_node)) => { // println!("{:?}", param_node); let keyword_locate = param_node.nodes.0.nodes.0; // println!("keyword {:#?}", keyword_locate); let (start_line, start_character) = (id.line, get_column_by_offset(&content, keyword_locate.offset) as u32); let (mut end_line, mut end_character) = (id.line, start_character + id.len as u32); let net_type = match unwrap_node!(param_node, DataType) { Some(RefNode::DataType(data_type)) => { let id = unwrap_node!(data_type, SimpleIdentifier, Keyword); if id == None { "wire" } else { let id = get_identifier(id.unwrap()).unwrap(); syntax_tree.get_str(&id).unwrap() } } _ => "wire" }; let init = if let Some(init) = unwrap_node!(param_node, ConstantMintypmaxExpression) { match init { RefNode::ConstantMintypmaxExpression(expression) => { // println!("expression {:?}", x); let param_init = sv_parser::NeedParseExpression::Parameter(expression.clone()); let (exp, last_locate) = parse_expression(&syntax_tree, ¶m_init); (end_line, end_character) = if last_locate != None { // println!("param {:?} lastlocate {:?}", name, last_locate); (last_locate.unwrap().line, (get_column_by_offset(&content, last_locate.unwrap().offset) + last_locate.unwrap().len) as u32) } else { (end_line, end_character) }; // println!("end pos {} {}", end_line, end_character); exp } _ => "unknown".to_string() } } else { "unknown".to_string() }; hdlparam.add_parameter(name, net_type, init.as_str(), start_line, start_character, end_line, end_character); } _ => () } } } RefNode::PortDeclaration(x) => { if let Some(id) = unwrap_node!(x, InputDeclaration, OutputDeclaration, InoutDeclaration) { let id = get_identifier(id).unwrap(); let dir_type = syntax_tree.get_str(&id).unwrap(); let (dir_line, dir_character) = (id.line, get_column_by_offset(&content, id.offset) as u32); let net_type = match unwrap_node!(x, DataType, ImplicitDataType) { Some(RefNode::DataType(x)) => { let id = unwrap_node!(x, Keyword); if id != None { syntax_tree.get_str(&get_identifier(id.unwrap()).unwrap()).unwrap() } else { "unknown" } }, Some(RefNode::ImplicitDataType(_)) => "wire", _ => "unknown" }; let width = match unwrap_node!(x, PackedDimensionRange) { Some(RefNode::PackedDimensionRange(x)) => { let port_width = sv_parser::NeedParseExpression::Port(x.clone()); let (width, _) = parse_expression(&syntax_tree, &port_width); width } _ => "1".to_string() }; if let Some(RefNode::ListOfPortIdentifiers(x)) = unwrap_node!(x, ListOfPortIdentifiers) { for node in x { if let RefNode::PortIdentifier(x) = node { let id = unwrap_node!(x, Identifier).unwrap(); let id = get_identifier(id).unwrap(); let name = syntax_tree.get_str(&id).unwrap(); let (start_line, start_character, end_line, end_character) = (dir_line, dir_character, id.line, (get_column_by_offset(&content, id.offset) + id.len) as u32); hdlparam.add_port(name, dir_type, net_type, width.as_str(), start_line, start_character, end_line, end_character); } } } } } RefNode::AnsiPortDeclaration(x) => { if let Some(id) = unwrap_node!(x, PortIdentifier) { let name_locate = get_identifier(id).unwrap(); let name = syntax_tree.get_str(&name_locate).unwrap(); let character = get_column_by_offset(&content, name_locate.offset); let (end_line, end_character) = (name_locate.line, (character + name_locate.len) as u32); let id = unwrap_node!(x, PortDirection); let (start_line, start_character) = if id != None { let id = id.unwrap(); let dir_locate = get_identifier(id).unwrap(); ansi_port_last_dir = syntax_tree.get_str(&dir_locate).unwrap(); (dir_locate.line, get_column_by_offset(&content, dir_locate.offset) as u32) } else { (name_locate.line, character as u32) }; let net_type = if unwrap_node!(x, AnsiPortDeclarationVariable) != None { "wire" } else { match unwrap_node!(x, DataType, ImplicitDataType) { Some(RefNode::DataType(x)) => { let id = unwrap_node!(x, Keyword); if id != None { syntax_tree.get_str(&get_identifier(id.unwrap()).unwrap()).unwrap() } else { "unknown" } }, Some(RefNode::ImplicitDataType(_)) => "wire", _ => "unknown" } }; let width = match unwrap_node!(x, PackedDimensionRange) { Some(RefNode::PackedDimensionRange(x)) => { let port_width = sv_parser::NeedParseExpression::Port(x.clone()); let (width, _) = parse_expression(&syntax_tree, &port_width); width } _ => "1".to_string() }; hdlparam.add_port(name, ansi_port_last_dir, net_type, width.as_str(), start_line, start_character, end_line, end_character); } } 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(); if let Some(id) = unwrap_node!(x, HierarchicalInstance) { let id = get_identifier(id).unwrap(); let name = syntax_tree.get_str(&id).unwrap(); let (line, character) = (id.line, get_column_by_offset(&content, id.offset) as u32); let end_character = character + id.len as u32; let (param_start_line, param_start_character, param_end_line, param_end_character) = match unwrap_node!(x, ListOfParameterAssignments) { Some(RefNode::ListOfParameterAssignments(x)) => { let param_assignments = sv_parser::NeedGetLocate::ParamAssignments(x.clone()); get_port_parameter_range(&content, ¶m_assignments) } _ => (0, 0, 0, 0) }; let (port_start_line, port_start_character, port_end_line, port_end_character) = match unwrap_node!(x, ListOfPortConnections) { Some(RefNode::ListOfPortConnections(x)) => { let param_assignments = sv_parser::NeedGetLocate::PortConnections(x.clone()); get_port_parameter_range(&content, ¶m_assignments) } _ => (0, 0, 0, 0) }; hdlparam.add_instance(name, inst_type, line, character, end_character, param_start_line, param_start_character, param_end_line, param_end_character, port_start_line, port_start_character, port_end_line, port_end_character ); } } } 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(); match unwrap_node!(x, NInputGateInstance, NOutputGateInstance) { Some(id) => { let id = get_identifier(id).unwrap(); let name = syntax_tree.get_str(&id).unwrap(); let (line, character) = (id.line, get_column_by_offset(&content, id.offset) as u32); let end_character = character + id.len as u32; let gate_instance = sv_parser::NeedGetLocate::GateInstantiation(x.clone()); let (param_start_line, param_start_character, param_end_line, param_end_character) = (0, 0, 0, 0); let (port_start_line, port_start_character, port_end_line, port_end_character) = get_port_parameter_range(&content, &gate_instance); hdlparam.add_instance(name, inst_type, line, character, end_character, param_start_line, param_start_character, param_end_line, param_end_character, port_start_line, port_start_character, port_end_line, port_end_character ); } _ => () } } _ => () } } // update_module_range(&path, &mut hdlparam); Ok(hdlparam) } fn get_port_parameter_range(content: &[String], x: &sv_parser::NeedGetLocate) -> (u32, u32, u32, u32) { let locate = get_first_last_locate(&x); if locate.is_none() { (0,0,0,0) } else { ( locate.unwrap().0.line, get_column_by_offset(&content, locate.unwrap().0.offset) as u32, locate.unwrap().1.line, (get_column_by_offset(&content, locate.unwrap().1.offset) + locate.unwrap().1.len) as u32 ) } } fn get_first_last_locate(x: &sv_parser::NeedGetLocate) -> Option<(Locate, Locate)> { let mut first_locate = Locate { offset: 0, line: 0, len: 0 }; let mut last_locate = Locate { offset: 0, line: 0, len: 0 }; match x { sv_parser::NeedGetLocate::GateInstantiation(gate) => { for node in gate { match unwrap_node!(node, InputTerminal, OutputTerminal) { Some(RefNode::InputTerminal(x)) => { if let Some(id) = unwrap_node!(x, Identifier) { let locate = get_identifier(id).unwrap(); if locate != last_locate { last_locate = locate; } if first_locate.offset == 0 { first_locate = locate; }; } else if let Some(RefNode::Expression(x)) = unwrap_node!(x, Expression) { let exp = sv_parser::NeedParseExpression::Expression(x.clone()); if let Some(locate) = parse_expression_only_locate(&exp) { if locate != last_locate { last_locate = locate; } if first_locate.offset == 0 { first_locate = locate; }; } } } Some(RefNode::OutputTerminal(x)) => { if let Some(id) = unwrap_node!(x, Identifier) { let locate = get_identifier(id).unwrap(); if locate != last_locate { last_locate = locate; } if first_locate.offset == 0 { first_locate = locate; }; } else if let Some(RefNode::Expression(x)) = unwrap_node!(x, Expression) { let exp = sv_parser::NeedParseExpression::Expression(x.clone()); if let Some(locate) = parse_expression_only_locate(&exp) { if locate != last_locate { last_locate = locate; } if first_locate.offset == 0 { first_locate = locate; }; } } } _ => () } } } _ => { for node in x { match unwrap_node!(node, SimpleIdentifier, Symbol) { Some(RefNode::SimpleIdentifier(x)) => { let locate = x.nodes.0; if locate != last_locate { last_locate = locate; } if first_locate.offset == 0 { first_locate = locate; }; } Some(RefNode::Symbol(x)) => { let locate = x.nodes.0; if locate != last_locate { last_locate = locate; } if first_locate.offset == 0 { first_locate = locate; }; } _ => () } } } } if last_locate.offset == 0 { None } else { Some((first_locate, last_locate)) } } fn parse_expression_only_locate(x: &sv_parser::NeedParseExpression) -> Option { let mut last_locate = Locate { offset: 0, line: 0, len: 0 }; for node in x { // println!("parse expression::node {:#?}", node); match unwrap_node!(node, SimpleIdentifier, Symbol, UnsignedNumber, HexNumber, OctalNumber, BinaryNumber) { Some(RefNode::SimpleIdentifier(x)) => { let locate = x.nodes.0; if locate != last_locate { last_locate = locate; } } Some(RefNode::Symbol(x)) => { let locate = x.nodes.0; if locate != last_locate { last_locate = locate; } } Some(RefNode::UnsignedNumber(x)) => { let locate = x.nodes.0; if locate != last_locate { last_locate = locate; } } Some(RefNode::HexNumber(x)) => { let locate = x.nodes.1.nodes.0; if locate != last_locate { last_locate = locate; } } Some(RefNode::OctalNumber(x)) => { let locate = x.nodes.1.nodes.0; if locate != last_locate { last_locate = locate; } } Some(RefNode::BinaryNumber(x)) => { let locate = x.nodes.1.nodes.0; if locate != last_locate { last_locate = locate; } } _ => () } } if last_locate.offset == 0 { Some(last_locate) } else { None } } fn parse_expression(syntax_tree: &SyntaxTree, x: &sv_parser::NeedParseExpression) -> (String, Option) { let mut last_locate = Locate { offset: 0, line: 0, len: 0 }; let mut expression = String::new(); for node in x { // println!("parse expression::node {:#?}", node); match unwrap_node!(node, SimpleIdentifier, Symbol, UnsignedNumber, HexNumber, OctalNumber, BinaryNumber) { Some(RefNode::SimpleIdentifier(x)) => { let locate = x.nodes.0; if locate != last_locate { last_locate = locate; let s = syntax_tree.get_str(&locate).unwrap(); expression = expression + s; // println!("parse expression {}", s); } } Some(RefNode::Symbol(x)) => { let locate = x.nodes.0; if locate != last_locate { last_locate = locate; let s = syntax_tree.get_str(&x.nodes.0).unwrap(); expression = expression + s; // println!("parse expression {}", s); } } Some(RefNode::UnsignedNumber(x)) => { let locate = x.nodes.0; if locate != last_locate { last_locate = locate; let s = syntax_tree.get_str(&x.nodes.0).unwrap(); expression = expression + s; // println!("parse expression {}", s); } } Some(RefNode::HexNumber(x)) => { let locate = x.nodes.1.nodes.0; if locate != last_locate { last_locate = locate; let size = if x.nodes.0 != None { syntax_tree.get_str(&x.nodes.0).unwrap() } else { "" }; let base = syntax_tree.get_str(&x.nodes.1.nodes.0).unwrap(); let number = syntax_tree.get_str(&x.nodes.2.nodes.0).unwrap(); expression = expression + size + base + number; // println!("parse expression {}", expression); } } Some(RefNode::OctalNumber(x)) => { let locate = x.nodes.1.nodes.0; if locate != last_locate { last_locate = locate; let size = if x.nodes.0 != None { syntax_tree.get_str(&x.nodes.0).unwrap() } else { "" }; let base = syntax_tree.get_str(&x.nodes.1.nodes.0).unwrap(); let number = syntax_tree.get_str(&x.nodes.2.nodes.0).unwrap(); expression = expression + size + base + number; // println!("parse expression {}", expression); } } Some(RefNode::BinaryNumber(x)) => { let locate = x.nodes.1.nodes.0; if locate != last_locate { last_locate = locate; let size = if x.nodes.0 != None { syntax_tree.get_str(&x.nodes.0).unwrap() } else { "" }; let base = syntax_tree.get_str(&x.nodes.1.nodes.0).unwrap(); let number = syntax_tree.get_str(&x.nodes.2.nodes.0).unwrap(); expression = expression + size + base + number; // println!("parse expression {}", expression); } } _ => () } } if expression == "" { ("unknown".to_string(), None) } else { // println!("parse function lastlocate {:?}", last_locate); (expression, Some(last_locate)) } } fn get_identifier(node: RefNode) -> Option { // unwrap_node! can take multiple types match unwrap_node!(node, SimpleIdentifier, EscapedIdentifier, Keyword) { Some(RefNode::SimpleIdentifier(x)) => { return Some(x.nodes.0); } Some(RefNode::EscapedIdentifier(x)) => { return Some(x.nodes.0); } Some(RefNode::Keyword(x)) => { return Some(x.nodes.0); } _ => None, } } fn get_includes(path: &PathBuf) -> Vec { let mut includes = Vec::new(); let file = File::open(path).unwrap(); let reader = BufReader::new(file); for (line_number, line_content) in reader.lines().enumerate() { let line_content = line_content.unwrap(); if line_content.trim().starts_with("`include") { let parts: Vec<&str> = line_content.split_whitespace().collect(); if parts.len() >= 2 { let path = parts[1]; let last_character = line_content.find(path).unwrap() + path.len(); includes.push(crate::core::hdlparam::Include { path: path.to_string(), range: crate::core::hdlparam::Range { start: crate::core::hdlparam::Position { line: (line_number + 1) as u32, character: 1 }, end: crate::core::hdlparam::Position { line: (line_number + 1) as u32, character: last_character as u32 } } }); } } } includes } #[allow(unused)] fn update_module_range(path: &PathBuf, hdlparam: &mut FastHdlparam) { let file = File::open(path).unwrap(); let reader = BufReader::new(file); let re_module = Regex::new(r"^\s*module\s+(\w+)\s*\(").unwrap(); let re_endmodule = Regex::new(r"^\s*endmodule\s*$").unwrap(); let mut current_offset = 0; let mut module_stack: Vec = Vec::new(); for (line_number, line_content) in reader.lines().enumerate() { match line_content { Ok(line) => { let line_length = line.len() + 1; // +1 for newline character current_offset += line_length; if let Some(captures) = re_module.captures(&line) { let module_name = captures.get(1).unwrap().as_str().to_string(); module_stack.push(module_name.clone()); } else if re_endmodule.is_match(&line) { if let Some(module_name) = module_stack.pop() { hdlparam.update_module_range(&module_name, (line_number + 1) as u32, current_offset as u32); // println!("Module {} ends.", module_name); } } } _ => () } } } fn get_column_by_offset(content: &[String], offset: usize) -> usize { let mut current_offset = 0; for line_content in content { let line_length = line_content.len() + 1; // +1 for newline character // println!("now line {} len {}", line_content, line_length); // println!("now offset {} offset {}", current_offset + line_length, offset); if current_offset + line_length > offset { return offset - current_offset + 1; } current_offset += line_length; } 1 // if offset over the file lentgh,return 1 } #[cfg(test)] mod tests { use std::{fs, path::Path}; use super::sv_parser; const TESTFILES_DIR: &str = "testfiles"; const DIGTIAL_IDE_TEST: &str = "/home/dide/project/Digital-Test/Digital-IDE-test/user"; macro_rules! unwrap_result { ($expr:expr) => { match $expr { Ok(e) => e, Err(_) => return } }; } macro_rules! unwrap_option { ($expr:expr) => { match $expr { Some(e) => e, None => return } }; } #[test] fn test_sv_parse() { let entries = unwrap_result!(fs::read_dir(TESTFILES_DIR)); for entry in entries { let entry = unwrap_result!(entry); let file_path = entry.path(); if file_path.is_file() { let extension = unwrap_option!(file_path.extension()); let file_path = unwrap_option!(file_path.to_str()); if extension == "v" || extension == "sv" { let _ = sv_parser(file_path); } } } } #[test] fn test_digital_ide_test() { // 判断路径是否存在且为文件夹 let path = Path::new(DIGTIAL_IDE_TEST); 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 == "v" || ext == "sv" { println!("Test file: {:?}", path); // TODO: Check Stack Overflow tests if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/ivtest/comp1001.sv" { continue; } if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/ivtest/comp1000.sv" { continue; } if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/ivtest/br_gh330.sv" { continue; } if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/hdlconv/pri_encoder_using_assign.sv" { continue; } let file_path = path.to_str().unwrap(); let _ = sv_parser(file_path); } } } } } Ok(()) } }