digital-lsp-server/src/core/sv_parser.rs
2024-10-29 21:46:26 +08:00

867 lines
37 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use std::fs::{self, File};
use std::io::BufRead;
use std::io::BufReader;
use std::path::PathBuf;
use log::info;
use regex::Regex;
use ropey::Rope;
use tower_lsp::lsp_types::Url;
use sv_parser::{unwrap_locate, unwrap_node, EventIter, ListOfParamAssignments, Locate, NodeEvent, ParamAssignment, ParameterDeclaration, RefNode, SyntaxTree};
use crate::core::hdlparam::{AssignType, Position, Range};
use crate::sources::{recovery_sv_parse_with_retry, LSPSupport};
use crate::utils::to_escape_path;
use super::hdlparam::{self, FastHdlparam, InstPort, Macro};
macro_rules! advance_until_leave {
($tokens:ident, $tree:ident, $event_iter:ident, $node:path) => {{
let mut result: Option<RefNode> = None;
while let Some(event) = $event_iter.next() {
match event {
NodeEvent::Leave(x) => match x {
$node(node) => {
result = Some($node(node));
break;
}
RefNode::Locate(node) => {
$tokens.push(' ');
$tokens.push_str($tree.get_str(node).unwrap());
}
_ => (),
},
NodeEvent::Enter(_) => (),
}
}
result
}};
}
macro_rules! skip_until_enter {
($tree:ident, $event_iter:ident, $node:path, $type:ty) => {{
let mut result: Option<$type> = None;
while let Some(event) = $event_iter.next() {
match event {
NodeEvent::Enter(x) => match x {
$node(node) => {
result = Some(node);
break;
}
_ => (),
},
NodeEvent::Leave(_) => (),
}
}
result
}};
}
macro_rules! skip_until_leave {
($tree:ident, $event_iter:ident, $node:path) => {
while let Some(event) = $event_iter.next() {
match event {
NodeEvent::Enter(_) => (),
NodeEvent::Leave(x) => match x {
$node(_) => {
break;
}
_ => (),
},
}
}
};
}
#[allow(unused)]
pub fn sv_parser(path: &str) -> Option<FastHdlparam> {
// The path of SystemVerilog source file
let path = PathBuf::from(path);
// The list of include paths
let includes: Vec<PathBuf> = 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_with_retry(&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<FastHdlparam, std::io::Error> {
// 对不同操作系统文件路径的支持
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 = "";
// TODO: 删除这个 content
let content = syntax_tree.text.text().split('\n')
.map(|s| s.to_string())
.collect::<Vec<String>>();
// println!("{:?}", syntax_tree);
// &SyntaxTree is iterable
let doc = Rope::from_str(syntax_tree.text.text());
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(&param_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::ParameterDeclaration(param_dec) => {
let mut event_iter = param_dec.into_iter().event();
match param_dec {
ParameterDeclaration::Param(x) => {
let keyword_locate = &x.nodes.0.nodes.0;
// println!("keyword {:#?}", keyword_locate);
let (start_line, start_character) = (keyword_locate.line, get_column_by_offset(&content, keyword_locate.offset) as u32);
let net_type = match unwrap_node!(RefNode::ParameterDeclarationParam(x), 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"
};
for (index, (name, loc, init)) in list_param_assignment(syntax_tree, &x.nodes.2, &mut event_iter).iter().enumerate() {
let (start_line, start_character) = if index != 0 {
(loc.line as u32, get_column_by_offset(&content, loc.offset) as u32)
} else {
(start_line, start_character)
};
let (end_line, end_character) = (loc.line as u32, (get_column_by_offset(&content, loc.offset) + loc.len) as u32);
hdlparam.add_parameter(name, net_type, init, 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 hier_node = id.clone();
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, ParameterValueAssignment) {
Some(inst_node) => {
// get_port_parameter_range(&content, inst_node)
get_pp_range(&doc, inst_node)
}
_ => (0, 0, 0, 0)
};
let inst_port_assignments = get_instance_ports(&syntax_tree, &doc, hier_node.clone());
let (port_start_line, port_start_character, port_end_line, port_end_character) = get_pp_range(&doc, hier_node);
hdlparam.add_instance(name, inst_type, inst_port_assignments, 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 gate_node = id.clone();
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) = (0, 0, 0, 0);
let inst_port_assignments = get_instance_ports(&syntax_tree, &doc, gate_node.clone());
let (port_start_line, port_start_character, port_end_line, port_end_character) = get_pp_range(&doc, gate_node);
hdlparam.add_instance(name, inst_type, inst_port_assignments, 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)
}
/// 返回的四元组:(start_line, start_character, end_line, end_character)
fn get_port_parameter_range(content: &[String], node: RefNode) -> (u32, u32, u32, u32) {
let locate = get_first_last_locate(node);
if locate.is_none() {
( 0, 0, 0, 0 )
} else {
let locate = locate.unwrap();
let start_line = locate.0.line;
let start_character = get_column_by_offset(&content, locate.0.offset) as u32;
let end_line = locate.1.line;
let end_character = get_column_by_offset(&content, locate.1.offset + locate.1.len) as u32;
( start_line, start_character, end_line, end_character )
}
}
// 获取 port 或者 param 的 range
/// 返回的四元组:(start_line, start_character, end_line, end_character)
fn get_pp_range(doc: &Rope, node: RefNode) -> (u32, u32, u32, u32) {
if let Some(locate) = get_first_last_locate(node) {
let start_byte = locate.0.offset;
let end_byte = locate.1.offset + locate.1.len;
let start_pos = doc.byte_to_pos(start_byte);
let end_pos = doc.byte_to_pos(end_byte);
( start_pos.line, start_pos.character, end_pos.line, end_pos.character )
} else {
( 0, 0, 0, 0 )
}
}
fn get_first_last_locate(node: RefNode) -> 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 };
for n in node {
if let RefNode::Symbol(x) = n {
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 get_instance_ports(syntax_tree: &SyntaxTree, doc: &Rope, node: RefNode) -> Vec<InstPort> {
let mut ports = Vec::new();
for list in node {
match unwrap_node!(list, ListOfPortConnectionsNamed, ListOfPortConnectionsOrdered, InputTerminal, OutputTerminal) {
Some(RefNode::ListOfPortConnectionsNamed(x)) => {
for connect in x {
if let RefNode::NamedPortConnectionIdentifier(ident) = connect {
let (start_line, start_character, end_line, end_character) = get_pp_range(doc, connect);
let port_name_locate = get_identifier(RefNode::PortIdentifier(&(ident.nodes.2)));
let port_name = syntax_tree.get_str(&port_name_locate).unwrap();
let name = if ident.nodes.3.is_some() {
if let (_, Some(name_expr), _) = ident.nodes.3.clone().unwrap().nodes {
let expr = sv_parser::NeedParseExpression::Expression(name_expr);
let (expr, _) = parse_expression(syntax_tree, &expr);
expr
} else {
"".to_string()
}
} else {
"".to_string()
};
let port = InstPort {
port: Some(port_name.to_string()),
assign_name: name,
assign_type: AssignType::Named,
range: Range {
start: Position { line: start_line, character: start_character },
end: Position { line: end_line, character: end_character }
}
};
ports.push(port);
};
}
}
Some(RefNode::ListOfPortConnectionsOrdered(x)) => {
for connect in x {
if let RefNode::OrderedPortConnection(ident) = connect {
if let Some(expr) = ident.nodes.1.clone() {
let start_locate = get_identifier(RefNode::Expression(&expr));
let expr = sv_parser::NeedParseExpression::Expression(expr);
let (expr, locate) = parse_expression(syntax_tree, &expr);
let start_locate = start_locate.unwrap_or(Locate { offset: 0, line: 0, len: 0 });
let locate = locate.unwrap_or(start_locate);
let port = InstPort {
port: None,
assign_name: expr,
assign_type: AssignType::Ordered,
range: Range {
start: get_position(doc, start_locate),
end: get_position(doc, Locate {
offset: locate.offset + locate.len,
line: locate.line,
len: 0
})
}
};
ports.push(port);
}
};
}
}
Some(RefNode::InputTerminal(x)) => {
let expr = x.nodes.0.clone();
let start_locate = get_identifier(RefNode::Expression(&expr));
let expr = sv_parser::NeedParseExpression::Expression(expr);
let (expr, locate) = parse_expression(syntax_tree, &expr);
let start_locate = start_locate.unwrap_or(Locate { offset: 0, line: 0, len: 0 });
let locate = locate.unwrap_or(start_locate);
let port = InstPort {
port: None,
assign_name: expr,
assign_type: AssignType::Ordered,
range: Range {
start: get_position(doc, start_locate),
end: get_position(doc, Locate {
offset: locate.offset + locate.len,
line: locate.line,
len: 0
})
}
};
ports.push(port);
}
Some(RefNode::OutputTerminal(x)) => {
let expr = x.nodes.0.clone();
let start_locate = get_identifier(RefNode::NetLvalue(&expr));
let expr = sv_parser::NeedParseExpression::NetValue(expr);
let (expr, locate) = parse_expression(syntax_tree, &expr);
let start_locate = start_locate.unwrap_or(Locate { offset: 0, line: 0, len: 0 });
let locate = locate.unwrap_or(start_locate);
let port = InstPort {
port: None,
assign_name: expr,
assign_type: AssignType::Ordered,
range: Range {
start: get_position(doc, start_locate),
end: get_position(doc, Locate {
offset: locate.offset + locate.len,
line: locate.line,
len: 0
})
}
};
ports.push(port);
}
_ => ()
}
}
ports.sort_by_key(|p| p.range.clone());
ports.dedup_by_key(|p| p.range.clone());
ports
}
fn parse_expression(syntax_tree: &SyntaxTree, x: &sv_parser::NeedParseExpression) -> (String, Option<Locate>) {
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<Locate> {
// 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_position(doc: &Rope, locate: Locate) -> Position {
let byte = locate.offset;
let pos = doc.byte_to_pos(byte);
hdlparam::Position::from_lsp_position(&pos)
}
fn get_includes(path: &PathBuf) -> Vec<crate::core::hdlparam::Include> {
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 = match line_content {
Ok(content) => content,
Err(e) => {
println!("line {} has error {}", line_number, e);
"".to_string()
}
};
if line_content.trim().starts_with("`include") {
let parts: Vec<&str> = line_content.split_whitespace().collect();
if parts.len() >= 2 {
let mut path = parts[1].trim();
if path.starts_with("\"") {
path = path.strip_prefix("\"").unwrap();
}
if path.ends_with("\"") {
path = path.strip_suffix("\"").unwrap();
}
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<String> = 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 lentghreturn 1
}
fn param_assignment(
tree: &SyntaxTree,
_: &ParamAssignment,
event_iter: &mut EventIter,
) -> Option<(String, Locate, String)> {
if let Some(param_assign) =
skip_until_enter!(tree, event_iter, RefNode::ParamAssignment, &ParamAssignment) {
let ident = get_ident(tree, RefNode::ParameterIdentifier(&param_assign.nodes.0));
let mut type_str = String::new();
advance_until_leave!(type_str, tree, event_iter, RefNode::ParamAssignment);
let parts: Vec<&str> = type_str.split_whitespace().collect();
type_str = if let Some(last_part) = parts.last() {
last_part.to_string()
} else {
"unknown".to_string()
};
Some((ident.0, ident.1, type_str))
} else {
None
}
}
fn list_param_assignment(
tree: &SyntaxTree,
_: &ListOfParamAssignments,
event_iter: &mut EventIter,
) -> Vec<(String, Locate, String)>{
let mut params = Vec::new();
// let mut defs: Vec<GenericDec> = Vec::new();
if let Some(p_a_list) = skip_until_enter!(
tree,
event_iter,
RefNode::ListOfParamAssignments,
&ListOfParamAssignments
) {
for param_assign in p_a_list.nodes.0.contents() {
if let Some(assignment) = param_assignment(tree, param_assign, event_iter) {
params.push(assignment);
}
}
};
params
}
/// 找到 node 的 名字,开始的位置和结束的位置
pub fn get_ident(tree: &SyntaxTree, node: RefNode) -> (String, Locate) {
let loc = unwrap_locate!(node).unwrap();
let ident_str = tree.get_str(loc).unwrap().to_string();
(ident_str, *loc)
}
#[cfg(test)]
mod tests {
use std::{fs, path::Path};
use super::sv_parser;
const TESTFILES_DIR: &str = "/home/dide/project/digital-lsp-server/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<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 == "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(())
}
}