Refactor TextMacroUsage
This commit is contained in:
parent
a6789b9752
commit
65abee8270
@ -78,6 +78,7 @@ pub(crate) fn include_compiler_directive(s: Span) -> IResult<Span, IncludeCompil
|
|||||||
alt((
|
alt((
|
||||||
include_compiler_directive_double_quote,
|
include_compiler_directive_double_quote,
|
||||||
include_compiler_directive_angle_bracket,
|
include_compiler_directive_angle_bracket,
|
||||||
|
include_compiler_directive_text_macro_usage,
|
||||||
))(s)
|
))(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,6 +114,22 @@ pub(crate) fn include_compiler_directive_angle_bracket(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracable_parser]
|
||||||
|
#[packrat_parser]
|
||||||
|
pub(crate) fn include_compiler_directive_text_macro_usage(
|
||||||
|
s: Span,
|
||||||
|
) -> IResult<Span, IncludeCompilerDirective> {
|
||||||
|
let (s, a) = symbol("`")(s)?;
|
||||||
|
let (s, b) = keyword("include")(s)?;
|
||||||
|
let (s, c) = text_macro_usage(s)?;
|
||||||
|
Ok((
|
||||||
|
s,
|
||||||
|
IncludeCompilerDirective::TextMacroUsage(Box::new(
|
||||||
|
IncludeCompilerDirectiveTextMacroUsage { nodes: (a, b, c) },
|
||||||
|
)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
#[tracable_parser]
|
#[tracable_parser]
|
||||||
#[packrat_parser]
|
#[packrat_parser]
|
||||||
pub(crate) fn angle_bracket_literal(s: Span) -> IResult<Span, AngleBracketLiteral> {
|
pub(crate) fn angle_bracket_literal(s: Span) -> IResult<Span, AngleBracketLiteral> {
|
||||||
|
@ -286,6 +286,7 @@ mod unit {
|
|||||||
test!(comment, "//", Ok((_, _)));
|
test!(comment, "//", Ok((_, _)));
|
||||||
test!(comment, "/* comment\n\n */", Ok((_, _)));
|
test!(comment, "/* comment\n\n */", Ok((_, _)));
|
||||||
test!(comment, "/* comment\n//aaa\n */", Ok((_, _)));
|
test!(comment, "/* comment\n//aaa\n */", Ok((_, _)));
|
||||||
|
test!(comment, "/*! comment\n * aaa\n */", Ok((_, _)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -316,6 +317,22 @@ mod unit {
|
|||||||
Ok((_, _))
|
Ok((_, _))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_regression() {
|
||||||
|
test!(
|
||||||
|
source_text,
|
||||||
|
r##"package pkg; localparam [5:0] RES = RES5[0]; endpackage"##,
|
||||||
|
Ok((_, _))
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
source_text,
|
||||||
|
r##"interface intf (); localparam TYPE DEFAULT = TYPE'(0); endinterface"##,
|
||||||
|
Ok((_, _))
|
||||||
|
);
|
||||||
|
test!(source_text, r##"`macro(A, B, logic, C)"##, Ok((_, _)));
|
||||||
|
test!(source_text, r##"`macro(A, B, logic, a())"##, Ok((_, _)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod spec {
|
mod spec {
|
||||||
@ -15787,6 +15804,10 @@ mod spec {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn debug() {
|
fn debug() {
|
||||||
test!(source_text, r##"/* comment\n//aaa\n */"##, Ok((_, _)));
|
test!(
|
||||||
|
source_text,
|
||||||
|
r##"interface intf (); localparam TYPE DEFAULT = TYPE'(0); endinterface"##,
|
||||||
|
Ok((_, _))
|
||||||
|
);
|
||||||
nom_tracable::cumulative_histogram();
|
nom_tracable::cumulative_histogram();
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use std::io::{BufReader, Read};
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use sv_parser_error::{Error, ErrorKind};
|
use sv_parser_error::{Error, ErrorKind};
|
||||||
use sv_parser_parser::{pp_parser, Span, SpanInfo};
|
use sv_parser_parser::{pp_parser, Span, SpanInfo};
|
||||||
use sv_parser_syntaxtree::{IncludeCompilerDirective, Locate, NodeEvent, RefNode};
|
use sv_parser_syntaxtree::{IncludeCompilerDirective, Locate, NodeEvent, RefNode, TextMacroUsage};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PreprocessedText {
|
pub struct PreprocessedText {
|
||||||
@ -76,7 +76,7 @@ pub struct Define {
|
|||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Defines = HashMap<String, Option<Define>>;
|
pub type Defines = HashMap<String, Option<Define>>;
|
||||||
|
|
||||||
pub fn preprocess<T: AsRef<Path>, U: AsRef<Path>>(
|
pub fn preprocess<T: AsRef<Path>, U: AsRef<Path>>(
|
||||||
path: T,
|
path: T,
|
||||||
@ -89,6 +89,15 @@ pub fn preprocess<T: AsRef<Path>, U: AsRef<Path>>(
|
|||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
reader.read_to_string(&mut s)?;
|
reader.read_to_string(&mut s)?;
|
||||||
|
|
||||||
|
preprocess_str(&s, path, pre_defines, include_paths)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn preprocess_str<T: AsRef<Path>, U: AsRef<Path>>(
|
||||||
|
s: &str,
|
||||||
|
path: T,
|
||||||
|
pre_defines: &Defines,
|
||||||
|
include_paths: &[U],
|
||||||
|
) -> Result<(PreprocessedText, Defines), Error> {
|
||||||
let mut skip = false;
|
let mut skip = false;
|
||||||
let mut skip_nodes = vec![];
|
let mut skip_nodes = vec![];
|
||||||
let mut defines = HashMap::new();
|
let mut defines = HashMap::new();
|
||||||
@ -223,19 +232,26 @@ pub fn preprocess<T: AsRef<Path>, U: AsRef<Path>>(
|
|||||||
defines.insert(id, Some(define));
|
defines.insert(id, Some(define));
|
||||||
}
|
}
|
||||||
NodeEvent::Enter(RefNode::IncludeCompilerDirective(x)) if !skip => {
|
NodeEvent::Enter(RefNode::IncludeCompilerDirective(x)) if !skip => {
|
||||||
let path = match x {
|
let mut path = match x {
|
||||||
IncludeCompilerDirective::DoubleQuote(x) => {
|
IncludeCompilerDirective::DoubleQuote(x) => {
|
||||||
let (_, _, ref literal) = x.nodes;
|
let (_, _, ref literal) = x.nodes;
|
||||||
let (locate, _) = literal.nodes;
|
let (locate, _) = literal.nodes;
|
||||||
locate.str(&s).trim_matches('"')
|
let p = locate.str(&s).trim_matches('"');
|
||||||
|
PathBuf::from(p)
|
||||||
}
|
}
|
||||||
IncludeCompilerDirective::AngleBracket(x) => {
|
IncludeCompilerDirective::AngleBracket(x) => {
|
||||||
let (_, _, ref literal) = x.nodes;
|
let (_, _, ref literal) = x.nodes;
|
||||||
let (locate, _) = literal.nodes;
|
let (locate, _) = literal.nodes;
|
||||||
locate.str(&s).trim_start_matches('<').trim_end_matches('>')
|
let p = locate.str(&s).trim_start_matches('<').trim_end_matches('>');
|
||||||
|
PathBuf::from(p)
|
||||||
|
}
|
||||||
|
IncludeCompilerDirective::TextMacroUsage(x) => {
|
||||||
|
let (_, _, ref x) = x.nodes;
|
||||||
|
let (p, _, _, _) =
|
||||||
|
resolve_text_macro_usage(x, s, path.as_ref(), &defines, include_paths)?;
|
||||||
|
PathBuf::from(p)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut path = PathBuf::from(path);
|
|
||||||
if path.is_relative() {
|
if path.is_relative() {
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
for include_path in include_paths {
|
for include_path in include_paths {
|
||||||
@ -253,55 +269,10 @@ pub fn preprocess<T: AsRef<Path>, U: AsRef<Path>>(
|
|||||||
ret.merge(include);
|
ret.merge(include);
|
||||||
}
|
}
|
||||||
NodeEvent::Enter(RefNode::TextMacroUsage(x)) if !skip => {
|
NodeEvent::Enter(RefNode::TextMacroUsage(x)) if !skip => {
|
||||||
let (_, ref name, ref args) = x.nodes;
|
let (text, path, range, new_defines) =
|
||||||
let id = identifier((&name.nodes.0).into(), &s).unwrap();
|
resolve_text_macro_usage(x, s, path.as_ref(), &defines, include_paths)?;
|
||||||
|
ret.push(&text, path, range);
|
||||||
let mut actual_args = Vec::new();
|
defines = new_defines;
|
||||||
if let Some(args) = args {
|
|
||||||
let (_, ref args, _) = args.nodes;
|
|
||||||
let (ref args,) = args.nodes;
|
|
||||||
for arg in args.contents() {
|
|
||||||
let (ref arg,) = arg.nodes;
|
|
||||||
let arg: Locate = arg.try_into().unwrap();
|
|
||||||
let arg = arg.str(&s);
|
|
||||||
actual_args.push(arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let define = defines.get(&id);
|
|
||||||
if let Some(Some(define)) = define {
|
|
||||||
let mut arg_map = HashMap::new();
|
|
||||||
for (i, (arg, default)) in define.arguments.iter().enumerate() {
|
|
||||||
let value = if let Some(actual_arg) = actual_args.get(i) {
|
|
||||||
*actual_arg
|
|
||||||
} else {
|
|
||||||
if let Some(default) = default {
|
|
||||||
default
|
|
||||||
} else {
|
|
||||||
return Err(ErrorKind::Preprocess.into());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
arg_map.insert(String::from(arg), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some((ref text, ref range)) = define.text {
|
|
||||||
let mut replaced = String::from("");
|
|
||||||
for text in split_text(&text) {
|
|
||||||
if let Some(value) = arg_map.get(&text) {
|
|
||||||
replaced.push_str(*value);
|
|
||||||
} else {
|
|
||||||
replaced.push_str(&text.replace("``", "").replace("\\\n", ""));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret.push(&replaced, define.path.clone(), *range);
|
|
||||||
} else {
|
|
||||||
return Err(ErrorKind::Preprocess.into());
|
|
||||||
}
|
|
||||||
} else if let Some(_) = define {
|
|
||||||
return Err(ErrorKind::Preprocess.into());
|
|
||||||
} else {
|
|
||||||
return Err(ErrorKind::Preprocess.into());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
NodeEvent::Enter(x) => {
|
NodeEvent::Enter(x) => {
|
||||||
if skip_nodes.contains(&x) {
|
if skip_nodes.contains(&x) {
|
||||||
@ -352,9 +323,77 @@ fn split_text(s: &str) -> Vec<String> {
|
|||||||
|
|
||||||
x.push(c);
|
x.push(c);
|
||||||
}
|
}
|
||||||
|
ret.push(x);
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_text_macro_usage<T: AsRef<Path>, U: AsRef<Path>>(
|
||||||
|
x: &TextMacroUsage,
|
||||||
|
s: &str,
|
||||||
|
path: T,
|
||||||
|
defines: &Defines,
|
||||||
|
include_paths: &[U],
|
||||||
|
) -> Result<(String, PathBuf, Range, Defines), Error> {
|
||||||
|
let (_, ref name, ref args) = x.nodes;
|
||||||
|
let id = identifier((&name.nodes.0).into(), &s).unwrap();
|
||||||
|
|
||||||
|
let mut actual_args = Vec::new();
|
||||||
|
if let Some(args) = args {
|
||||||
|
let (_, ref args, _) = args.nodes;
|
||||||
|
let (ref args,) = args.nodes;
|
||||||
|
for arg in args.contents() {
|
||||||
|
let (ref arg,) = arg.nodes;
|
||||||
|
let arg = arg.str(&s);
|
||||||
|
actual_args.push(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let define = defines.get(&id);
|
||||||
|
if let Some(Some(define)) = define {
|
||||||
|
let mut arg_map = HashMap::new();
|
||||||
|
for (i, (arg, default)) in define.arguments.iter().enumerate() {
|
||||||
|
let value = if let Some(actual_arg) = actual_args.get(i) {
|
||||||
|
*actual_arg
|
||||||
|
} else {
|
||||||
|
if let Some(default) = default {
|
||||||
|
default
|
||||||
|
} else {
|
||||||
|
return Err(ErrorKind::DefineArgNotFound(String::from(arg)).into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
arg_map.insert(String::from(arg), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((ref text, ref range)) = define.text {
|
||||||
|
let mut replaced = String::from("");
|
||||||
|
for text in split_text(&text) {
|
||||||
|
if let Some(value) = arg_map.get(&text) {
|
||||||
|
replaced.push_str(*value);
|
||||||
|
} else {
|
||||||
|
replaced.push_str(&text.replace("``", "").replace("\\\n", ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// separator is required
|
||||||
|
replaced.push_str(" ");
|
||||||
|
|
||||||
|
let (replaced, new_defines) =
|
||||||
|
preprocess_str(&replaced, path.as_ref(), &defines, include_paths)?;
|
||||||
|
return Ok((
|
||||||
|
String::from(replaced.text()),
|
||||||
|
define.path.clone(),
|
||||||
|
*range,
|
||||||
|
new_defines,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
return Err(ErrorKind::DefineTextNotFound(String::from(id)).into());
|
||||||
|
}
|
||||||
|
} else if let Some(_) = define {
|
||||||
|
return Err(ErrorKind::DefineTextNotFound(String::from(id)).into());
|
||||||
|
} else {
|
||||||
|
return Err(ErrorKind::DefineNotFound(String::from(id)).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -33,6 +33,7 @@ pub struct ResetallCompilerDirective {
|
|||||||
pub enum IncludeCompilerDirective {
|
pub enum IncludeCompilerDirective {
|
||||||
DoubleQuote(Box<IncludeCompilerDirectiveDoubleQuote>),
|
DoubleQuote(Box<IncludeCompilerDirectiveDoubleQuote>),
|
||||||
AngleBracket(Box<IncludeCompilerDirectiveAngleBracket>),
|
AngleBracket(Box<IncludeCompilerDirectiveAngleBracket>),
|
||||||
|
TextMacroUsage(Box<IncludeCompilerDirectiveTextMacroUsage>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Node)]
|
#[derive(Clone, Debug, PartialEq, Node)]
|
||||||
@ -45,6 +46,11 @@ pub struct IncludeCompilerDirectiveAngleBracket {
|
|||||||
pub nodes: (Symbol, Keyword, AngleBracketLiteral),
|
pub nodes: (Symbol, Keyword, AngleBracketLiteral),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Node)]
|
||||||
|
pub struct IncludeCompilerDirectiveTextMacroUsage {
|
||||||
|
pub nodes: (Symbol, Keyword, TextMacroUsage),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Node)]
|
#[derive(Clone, Debug, PartialEq, Node)]
|
||||||
pub struct AngleBracketLiteral {
|
pub struct AngleBracketLiteral {
|
||||||
pub nodes: (Locate, Vec<WhiteSpace>),
|
pub nodes: (Locate, Vec<WhiteSpace>),
|
||||||
@ -101,7 +107,7 @@ pub struct ListOfActualArguments {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Node)]
|
#[derive(Clone, Debug, PartialEq, Node)]
|
||||||
pub struct ActualArgument {
|
pub struct ActualArgument {
|
||||||
pub nodes: (Expression,),
|
pub nodes: (Locate,),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Node)]
|
#[derive(Clone, Debug, PartialEq, Node)]
|
||||||
|
@ -7,25 +7,32 @@ use sv_parser::parse_sv;
|
|||||||
struct Opt {
|
struct Opt {
|
||||||
pub files: Vec<PathBuf>,
|
pub files: Vec<PathBuf>,
|
||||||
|
|
||||||
#[structopt(short = "I", long = "include", multiple = true, number_of_values = 1)]
|
/// Include path
|
||||||
|
#[structopt(short = "i", long = "include", multiple = true, number_of_values = 1)]
|
||||||
pub includes: Vec<PathBuf>,
|
pub includes: Vec<PathBuf>,
|
||||||
|
|
||||||
/// Show syntax tree
|
/// Show syntax tree
|
||||||
#[structopt(short = "t", long = "tree")]
|
#[structopt(short = "t", long = "tree")]
|
||||||
pub tree: bool,
|
pub tree: bool,
|
||||||
|
|
||||||
|
/// Quiet
|
||||||
|
#[structopt(short = "q", long = "quiet")]
|
||||||
|
pub quiet: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let opt = Opt::from_args();
|
let opt = Opt::from_args();
|
||||||
|
let mut defines = HashMap::new();
|
||||||
for path in &opt.files {
|
for path in &opt.files {
|
||||||
let syntax_tree = parse_sv(&path, &HashMap::new(), &opt.includes);
|
match parse_sv(&path, &defines, &opt.includes) {
|
||||||
|
Ok((syntax_tree, new_defines)) => {
|
||||||
match syntax_tree {
|
|
||||||
Ok(x) => {
|
|
||||||
if opt.tree {
|
if opt.tree {
|
||||||
println!("{}", x);
|
println!("{}", syntax_tree);
|
||||||
|
}
|
||||||
|
defines = new_defines;
|
||||||
|
if !opt.quiet {
|
||||||
|
println!("parse succeeded: {:?}", path);
|
||||||
}
|
}
|
||||||
println!("parse succeeded: {:?}", path);
|
|
||||||
}
|
}
|
||||||
Err(x) => {
|
Err(x) => {
|
||||||
println!("parse failed: {:?} ({})", path, x);
|
println!("parse failed: {:?} ({})", path, x);
|
||||||
|
@ -6,7 +6,7 @@ use std::fmt;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use sv_parser_error::{Error, ErrorKind};
|
use sv_parser_error::{Error, ErrorKind};
|
||||||
use sv_parser_parser::{lib_parser, sv_parser, Span, SpanInfo};
|
use sv_parser_parser::{lib_parser, sv_parser, Span, SpanInfo};
|
||||||
use sv_parser_pp::preprocess::{preprocess, Define, PreprocessedText};
|
use sv_parser_pp::preprocess::{preprocess, Define, Defines, PreprocessedText};
|
||||||
pub use sv_parser_syntaxtree::*;
|
pub use sv_parser_syntaxtree::*;
|
||||||
|
|
||||||
pub struct SyntaxTree {
|
pub struct SyntaxTree {
|
||||||
@ -67,15 +67,18 @@ pub fn parse_sv<T: AsRef<Path>, U: AsRef<Path>>(
|
|||||||
path: T,
|
path: T,
|
||||||
pre_defines: &HashMap<String, Option<Define>>,
|
pre_defines: &HashMap<String, Option<Define>>,
|
||||||
include_paths: &[U],
|
include_paths: &[U],
|
||||||
) -> Result<SyntaxTree, Error> {
|
) -> Result<(SyntaxTree, Defines), Error> {
|
||||||
let (text, _) = preprocess(path, pre_defines, include_paths)?;
|
let (text, defines) = preprocess(path, pre_defines, include_paths)?;
|
||||||
let span = Span::new_extra(text.text(), SpanInfo::default());
|
let span = Span::new_extra(text.text(), SpanInfo::default());
|
||||||
let result = all_consuming(sv_parser)(span);
|
let result = all_consuming(sv_parser)(span);
|
||||||
match result {
|
match result {
|
||||||
Ok((_, x)) => Ok(SyntaxTree {
|
Ok((_, x)) => Ok((
|
||||||
node: x.into(),
|
SyntaxTree {
|
||||||
text,
|
node: x.into(),
|
||||||
}),
|
text,
|
||||||
|
},
|
||||||
|
defines,
|
||||||
|
)),
|
||||||
Err(_) => Err(ErrorKind::Parse.into()),
|
Err(_) => Err(ErrorKind::Parse.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,15 +87,18 @@ pub fn parse_lib<T: AsRef<Path>, U: AsRef<Path>>(
|
|||||||
path: T,
|
path: T,
|
||||||
pre_defines: &HashMap<String, Option<Define>>,
|
pre_defines: &HashMap<String, Option<Define>>,
|
||||||
include_paths: &[U],
|
include_paths: &[U],
|
||||||
) -> Result<SyntaxTree, Error> {
|
) -> Result<(SyntaxTree, Defines), Error> {
|
||||||
let (text, _) = preprocess(path, pre_defines, include_paths)?;
|
let (text, defines) = preprocess(path, pre_defines, include_paths)?;
|
||||||
let span = Span::new_extra(text.text(), SpanInfo::default());
|
let span = Span::new_extra(text.text(), SpanInfo::default());
|
||||||
let result = all_consuming(lib_parser)(span);
|
let result = all_consuming(lib_parser)(span);
|
||||||
match result {
|
match result {
|
||||||
Ok((_, x)) => Ok(SyntaxTree {
|
Ok((_, x)) => Ok((
|
||||||
node: x.into(),
|
SyntaxTree {
|
||||||
text,
|
node: x.into(),
|
||||||
}),
|
text,
|
||||||
|
},
|
||||||
|
defines,
|
||||||
|
)),
|
||||||
Err(_) => Err(ErrorKind::Parse.into()),
|
Err(_) => Err(ErrorKind::Parse.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user