digital-lsp-server/src/core/vhdl_parser.rs
2024-12-25 21:13:38 +08:00

326 lines
13 KiB
Rust

use std::path::PathBuf;
use log::info;
use vhdl_lang::ast::{ArchitectureBody, ConcurrentStatement, DesignFile, Designator, EntityDeclaration, InstantiatedUnit, InterfaceDeclaration, InterfaceList, LabeledConcurrentStatement, Mode, ModeIndication, Name};
use vhdl_lang::{kind_str, HasTokenSpan, Token, TokenAccess, TokenSpan, VHDLParser, VHDLStandard};
use super::hdlparam::*;
pub fn vhdl_parse_str(path: &PathBuf, code: &str) -> Option<DesignFile> {
let mut diagnostics = Vec::new();
let parser = VHDLParser::new(VHDLStandard::VHDL2008);
if let Ok((_, design_file)) = parser.parse_vhdl_str(code, path, &mut diagnostics) {
return Some(design_file);
}
None
}
pub fn make_fast_from_units(
arch_and_entity: Vec<(Option<(ArchitectureBody, Vec<Token>)>, Option<(EntityDeclaration, Vec<Token>)>)>,
) -> Option<FastHdlparam> {
let mut hdlparam = FastHdlparam {
fast_macro: Macro {
defines: Vec::new(),
errors: Vec::new(),
includes: Vec::new(),
invalid: Vec::new()
},
content: Vec::new(),
entitys: Vec::new(),
file_type: "common".to_string()
};
// info!("arch and entity {arch_and_entity:#?}");
arch_and_entity.iter().for_each(|units| {
match units {
(Some((arch, arch_tokens)), entity_units) => {
let name = if let Some((entity, _)) = entity_units {
entity.ident.tree.item.name_utf8()
} else {
"".to_string()
};
let arch_name = arch.ident.tree.item.name_utf8();
let range = get_range_from_token(
arch_tokens.get_token(arch.span().get_start_token()),
arch_tokens.get_token(arch.span().get_end_token())
);
hdlparam.new_vhdl_module(name, arch_name, range);
let instances = arch.statements.iter().filter(|statement| {
match statement.statement.item {
ConcurrentStatement::Instance(_) => true,
_ => false
}
})
.map(|statement| parse_instance(statement, arch_tokens))
.collect::<Vec<Instance>>();
if let Some(last_module) = hdlparam.content.last_mut() {
last_module.instances = instances
}
}
(None, Some((entity, entity_tokens))) => {
let name = entity.ident.tree.item.name_utf8();
let range = get_range_from_token(
entity_tokens.get_token(entity.span().get_start_token()),
entity_tokens.get_token(entity.span().get_end_token())
);
hdlparam.new_entity(name, range);
if let Some(param_list) = &entity.generic_clause {
parse_interface_list(param_list, &entity_tokens).iter().for_each(|(name, _, net_type, _, init, range)| {
hdlparam.add_entity_parameter(name, net_type, init, range.clone());
});
}
if let Some(port_list) = &entity.port_clause {
parse_interface_list(port_list, &entity_tokens).iter().for_each(|(name, dir_type, net_type, width, init, range)| {
hdlparam.add_entity_port(name, dir_type, net_type, width, range.clone());
});
}
}
(None, None) => ()
}
});
Some(hdlparam)
}
fn parse_instance(statement: &LabeledConcurrentStatement, tokens: &Vec<Token>) -> Instance {
let name = if let Some(tree) = &statement.label.tree { tree.item.name_utf8() } else { "unknown".to_string() };
let range = get_range_from_token(
tokens.get_token(statement.span().get_start_token()),
tokens.get_token(statement.span().get_end_token())
);
let mut parsed_instance = Instance {
name,
inst_type: "".to_string(),
instparams: None,
intstparam_assignments: Vec::new(),
instports: None,
intstport_assignments: Vec::new(),
range
};
match &statement.statement.item {
ConcurrentStatement::Instance(instance) => {
parsed_instance.inst_type = match &instance.unit {
InstantiatedUnit::Component(name) | InstantiatedUnit::Configuration(name) => {
parse_instance_name(name)
},
InstantiatedUnit::Entity(name, arch_name_ref) => {
let name = parse_instance_name(name);
let arch_name = if let Some(arch_name_ref) = arch_name_ref {
format!("({})", arch_name_ref.item.item.name_utf8())
} else {
"".to_string()
};
name + &arch_name
}
_ => "unknown".to_string()
};
if let Some(parameter_list) = &instance.generic_map {
parsed_instance.instparams = Some(get_range_from_token(
tokens.get_token(parameter_list.span().get_start_token()),
tokens.get_token(parameter_list.span().get_end_token())
));
parsed_instance.intstparam_assignments = parameter_list.list.items.iter().map(|association_ele| {
let formal = association_ele.formal.clone().and_then(|formal|
Some(parse_string_from_tokenspan(formal.span(), tokens))
).unwrap_or(
"unknown".to_string()
);
let actual = parse_string_from_tokenspan(association_ele.actual.span(), tokens);
let assign_range = if association_ele.formal.is_some() {
get_range_from_token(
tokens.get_token(association_ele.formal.clone().unwrap().span().get_start_token()),
tokens.get_token(association_ele.actual.span().get_end_token())
)
} else {
get_range_from_token(
tokens.get_token(association_ele.actual.span().get_start_token()),
tokens.get_token(association_ele.actual.span().get_end_token())
)
};
InstParameter { parameter: Some(formal), assign_val: Some(actual), assign_type: AssignType::Named, range: assign_range }
}).collect::<Vec<InstParameter>>();
}
if let Some(port_list) = &instance.port_map {
parsed_instance.instports = Some(get_range_from_token(
tokens.get_token(port_list.span().get_start_token()),
tokens.get_token(port_list.span().get_end_token())
));
parsed_instance.intstport_assignments = port_list.list.items.iter().map(|association_ele| {
let formal = association_ele.formal.clone().and_then(|formal|
Some(parse_string_from_tokenspan(formal.span(), tokens))
).unwrap_or(
"unknown".to_string()
);
let actual = parse_string_from_tokenspan(association_ele.actual.span(), tokens);
let assign_range = if association_ele.formal.is_some() {
get_range_from_token(
tokens.get_token(association_ele.formal.clone().unwrap().span().get_start_token()),
tokens.get_token(association_ele.actual.span().get_end_token())
)
} else {
get_range_from_token(
tokens.get_token(association_ele.actual.span().get_start_token()),
tokens.get_token(association_ele.actual.span().get_end_token())
)
};
InstPort { port: Some(formal), assign_val: Some(actual), assign_type: AssignType::Named, range: assign_range }
}).collect::<Vec<InstPort>>();
}
}
_ => ()
};
parsed_instance
}
fn parse_instance_name(name: &vhdl_lang::ast::token_range::WithTokenSpan<Name>) -> String {
match &name.item {
Name::Designator(designator) => {
match &designator.item {
Designator::Identifier(symbol) => symbol.name_utf8(),
_ => "unknown".to_string()
}
}
Name::Selected(_lib_name, designator_token) => {
match &designator_token.item.item {
Designator::Identifier(symbol) => symbol.name_utf8(),
_ => "unknown".to_string()
}
}
_ => "unknown".to_string()
}
}
fn parse_interface_list(list: &InterfaceList, tokens: &Vec<Token>) -> Vec<(String, String, String, String, String, Range)> {
let mut interface_list = Vec::new();
list.items.iter().for_each(|interface| {
let range = get_range_from_token(
tokens.get_token(interface.span().get_start_token()),
tokens.get_token(interface.span().get_end_token())
);
match interface {
InterfaceDeclaration::Object(object) => {
let name = object.idents.first().unwrap().tree.item.name_utf8();
let (dir_type, net_type, width, init) = match &object.mode {
ModeIndication::Simple(simple_mode) => {
let dir_type = if let Some(mode_token) = &simple_mode.mode {
match mode_token.item {
Mode::In => "in",
Mode::Out => "out",
Mode::Buffer | Mode::InOut => "inout",
Mode::Linkage => "unknown"
}.to_string()
} else {
"unknown".to_string()
};
let net_type = simple_mode.subtype_indication.type_mark.item.to_string();
let width = if let Some(constraint) = &simple_mode.subtype_indication.constraint {
parse_width_from_tokenspan(constraint.span(), tokens)
} else {
"1".to_string()
};
let init = if let Some(expression) = &simple_mode.expression {
parse_width_from_tokenspan(expression.span(), tokens)
} else {
"unknown".to_string()
};
(dir_type, net_type, width, init)
}
_ => ("unknown".to_string(), "unknown".to_string(), "unknown".to_string(), "unknown".to_string())
};
interface_list.push((name, dir_type, net_type, width, init, range));
}
_ => ()
}
});
interface_list
}
fn parse_string_from_tokenspan(span: TokenSpan, tokens: &Vec<Token>) -> String {
span.iter().map(|id| {
if let Some(token) = tokens.get_token(id) {
if get_value(token) == "None" {
kind_str(token.kind).to_string()
} else {
get_value(token)
}
} else {
"".to_string()
}
}).collect()
}
fn parse_width_from_tokenspan(span: TokenSpan, tokens: &Vec<Token>) -> String {
// skip '(' and ')'
span.iter().skip(1).take(span.len() - 2).map(|id| {
if let Some(token) = tokens.get_token(id) {
if get_value(token) == "None" {
if kind_str(token.kind) == "downto" || kind_str(token.kind) == "to" {
":".to_string()
} else {
kind_str(token.kind).to_string()
}
} else {
get_value(token)
}
} else {
"".to_string()
}
}).collect()
}
fn get_range_from_token(start_token: Option<&Token>, end_token: Option<&Token>) -> Range {
let start = if let Some(token) = start_token {
Position { line: token.pos.start().line, character: token.pos.start().character }
} else {
Position { line: 0, character: 0 }
};
let end = if let Some(token) = end_token {
Position { line: token.pos.end().line, character: token.pos.end().character }
} else {
Position { line: 0, character: 0 }
};
Range { start, end }
}
#[allow(unused)]
fn get_value(token: &Token) -> String {
match &token.value {
vhdl_lang::Value::Identifier(symbol) => {
return symbol.name_utf8();
}
vhdl_lang::Value::String(latin1_string) => {
return latin1_string.to_string();
}
vhdl_lang::Value::BitString(latin1_string, _) => {
return latin1_string.to_string().replace("\\", "");
}
vhdl_lang::Value::AbstractLiteral(latin1_string, _) => {
return latin1_string.to_string();
}
vhdl_lang::Value::Character(character) => {
return character.to_string();
}
vhdl_lang::Value::Text(latin1_string) => {
return latin1_string.to_string();
}
vhdl_lang::Value::None => {
return "None".to_string();
}
}
}