use crate::config::ConfigOption;
use crate::linter::{SyntaxRule, SyntaxRuleResult};
use regex::Regex;
use sv_parser::{NodeEvent, RefNode, SyntaxTree};
#[derive(Default)]
pub struct StyleKeywordDatatype {
re_split: Option<Regex>,
re_kw: Option<Regex>,
re_succ: Option<Regex>,
}
impl SyntaxRule for StyleKeywordDatatype {
fn check(
&mut self,
syntax_tree: &SyntaxTree,
event: &NodeEvent,
_option: &ConfigOption,
) -> SyntaxRuleResult {
if self.re_split.is_none() {
self.re_split = Some(Regex::new(r"(?P<kw>[\\$'BXZa-z_01]+)(?P<succ>(?s:.)*)").unwrap());
}
if self.re_kw.is_none() {
let keywords =
[ "byte" , "chandle"
, "event"
, "int"
, "integer"
, "logic"
, "longint"
, "real"
, "realtime"
, "ref"
, "reg"
, "shortint"
, "shortreal"
, "signed"
, "string"
, "supply0"
, "supply1"
, "time"
, "tri"
, "tri0"
, "tri1"
, "triand"
, "trior"
, "unsigned"
, "uwire"
, "void"
, "wand"
, "wire"
, "wor"
].join("|"); self.re_kw = Some(Regex::new(format!("^({})$", keywords).as_str()).unwrap());
}
if self.re_succ.is_none() {
self.re_succ = Some(Regex::new(r"^[ ]?$").unwrap());
}
let node = match event {
NodeEvent::Enter(x) => x,
NodeEvent::Leave(_) => {
return SyntaxRuleResult::Pass;
}
};
match node {
RefNode::Keyword(x) => {
let re_split = self.re_split.as_ref().unwrap();
let re_kw = self.re_kw.as_ref().unwrap();
let t = syntax_tree.get_str(*x).unwrap();
let caps = re_split.captures(&t).unwrap();
if re_kw.is_match(&caps[1]) {
let re_succ = self.re_succ.as_ref().unwrap();
if re_succ.is_match(&caps[2]) {
SyntaxRuleResult::Pass
} else {
SyntaxRuleResult::Fail
}
} else {
SyntaxRuleResult::Pass
}
}
_ => SyntaxRuleResult::Pass,
}
}
fn name(&self) -> String {
String::from("style_keyword_datatype")
}
fn hint(&self, _option: &ConfigOption) -> String {
String::from("Follow datatype keyword with a symbol or exactly 1 space.")
}
fn reason(&self) -> String {
String::from("Consistent use of whitespace enhances readability by reducing visual noise.")
}
}