digital-lsp-server/src/core/sv_parser.rs

686 lines
31 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::File;
use std::io::BufRead;
use std::{collections::HashMap, io::BufReader};
use std::path::PathBuf;
use anyhow::Error;
use percent_encoding::percent_decode_str;
use std::env::consts::OS;
use sv_parser::{parse_sv, unwrap_node, Locate, RefNode, SyntaxTree};
use super::fast_hdlparam::{FastHdlparam, Macro};
pub fn sv_parser(path: &str) -> Option<FastHdlparam> {
// The path of SystemVerilog source file
let path = PathBuf::from(path);
// The list of defined macros
let defines = HashMap::new();
// The list of include paths
let includes: Vec<PathBuf> = Vec::new();
let result = parse_sv(&path, &defines, &includes, false, true);
if let Ok((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, Error> {
// 对不同操作系统文件路径的支持
let decoded_path = percent_decode_str(path.to_str().unwrap()).decode_utf8_lossy();
let decoded_path_str = decoded_path.as_ref();
let path = match OS {
"windows" => {
// 去掉开头的斜杠
let trimmed_path_str = decoded_path_str.trim_start_matches('/');
PathBuf::from(trimmed_path_str)
},
_ => {
// 其他操作系统(如 Linux保持原样
PathBuf::from(decoded_path_str)
},
};
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::<Vec<String>>();
// 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(&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::fast_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, &param_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, &param_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, &param_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
);
}
_ => ()
}
}
_ => ()
}
}
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<Locate> {
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<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_includes(path: &PathBuf) -> Vec<crate::core::fast_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 = 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::fast_hdlparam::Include {
path: path.to_string(),
range: crate::core::fast_hdlparam::Range {
start: crate::core::fast_hdlparam::Position {
line: (line_number + 1) as u32, character: 1
},
end: crate::core::fast_hdlparam::Position {
line: (line_number + 1) as u32, character: last_character as u32
}
}
});
}
}
}
includes
}
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
}
#[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<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(())
}
}