diff --git a/src/main.rs b/src/main.rs index 9bd3a9c..62396de 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,11 @@ use std::collections::HashMap; use std::error::Error as StdError; use std::fs::File; -use std::io::Read; +use std::io::{self, Read, BufRead}; use std::path::PathBuf; use std::{cmp, process}; use structopt::StructOpt; -use sv_parser::{parse_sv, SyntaxTree, unwrap_node, Locate, RefNode, Define, DefineText}; +use sv_parser::{parse_sv, parse_sv_str, SyntaxTree, unwrap_node, Locate, RefNode, Define, DefineText}; use sv_parser_error; use sv_parser_syntaxtree::*; use enquote; @@ -33,7 +33,7 @@ struct Opt { /// Include whitespace in output syntax tree #[structopt(long = "include-whitespace")] pub include_whitespace: bool, - + /// Show the macro definitions after processing each file #[structopt(long = "show-macro-defs")] pub show_macro_defs: bool, @@ -44,7 +44,11 @@ struct Opt { /// Allow incomplete #[structopt(long = "allow_incomplete")] - pub allow_incomplete: bool + pub allow_incomplete: bool, + + /// pass file text through CIN rather than as a file + #[structopt(long = "use-stdin")] + pub use_stdin: bool } fn main() { @@ -54,13 +58,13 @@ fn main() { } fn run_opt( - opt: &Opt + opt: &Opt ) -> i32 { // read in define variables let mut defines = HashMap::new(); for define in &opt.defines { - let mut define = define.splitn(2, '='); + let mut define = define.splitn(2, '='); let ident = String::from(define.next().unwrap()); let text = if let Some(x) = define.next() { let x = enquote::unescape(x, None).unwrap(); @@ -70,33 +74,51 @@ fn run_opt( }; let define = Define::new(ident.clone(), vec![], text); defines.insert(ident, Some(define)); - } - + } + // flag to determine parsing status let mut exit_code = 0; - + if opt.files.len() > 1 && opt.use_stdin { + println!("multiple files inputs defined with use-stdin; only the first will be used"); + }; + // parse files println!("files:"); - for path in &opt.files { - match parse_sv(&path, &defines, &opt.includes, opt.ignore_include, opt.allow_incomplete) { + if opt.use_stdin { + let input = io::stdin().lock().lines().fold("".to_string(), |acc, line| { + acc + &line.unwrap() + "\n" + }); + + let path = &opt.files[0]; + /* + let stdin = io::stdin(); + if !stdin.is_terminal() { // v1.70.0 later + let input = io::read_to_string(stdin).expect("Can not read stdin"); + + println!("{}", input); + }*/ + + // we still need the path presumably for error reporting + match parse_sv_str(&input, &path, &defines, &opt.includes, opt.ignore_include, opt.allow_incomplete) { Ok((syntax_tree, new_defines)) => { - println!(" - file_name: {}", escape_str(path.to_str().unwrap())); - if !opt.full_tree { - println!(" defs:"); - analyze_defs(&syntax_tree); - } else { - println!(" syntax_tree:"); - print_full_tree(&syntax_tree, opt.include_whitespace); - } - // update the preprocessor state if desired - if !opt.separate { - defines = new_defines; - } - // show macro definitions if desired - if opt.show_macro_defs { - println!(" macro_defs:"); - show_macro_defs(&defines); - } + println!(" - file_name: {}", escape_str(path.to_str().unwrap())); + if !opt.full_tree { + println!(" defs:"); + analyze_defs(&syntax_tree); + } else { + println!(" syntax_tree:"); + print_full_tree(&syntax_tree, opt.include_whitespace); + } + // update the preprocessor state if desired + if !opt.separate { + defines = new_defines; + } + // show macro definitions if desired + if opt.show_macro_defs { + println!(" macro_defs:"); + show_macro_defs(&defines); + } + } Err(x) => { match x { @@ -113,11 +135,52 @@ fn run_opt( } } } - exit_code = 1; + exit_code = 1; + } + } + } else { + for path in &opt.files { + match parse_sv(&path, &defines, &opt.includes, opt.ignore_include, opt.allow_incomplete) { + Ok((syntax_tree, new_defines)) => { + println!(" - file_name: {}", escape_str(path.to_str().unwrap())); + if !opt.full_tree { + println!(" defs:"); + analyze_defs(&syntax_tree); + } else { + println!(" syntax_tree:"); + print_full_tree(&syntax_tree, opt.include_whitespace); + } + // update the preprocessor state if desired + if !opt.separate { + defines = new_defines; + } + // show macro definitions if desired + if opt.show_macro_defs { + println!(" macro_defs:"); + show_macro_defs(&defines); + } + } + Err(x) => { + match x { + sv_parser_error::Error::Parse(Some((origin_path, origin_pos))) => { + eprintln!("parse failed: {:?}", path); + print_parse_error(&origin_path, &origin_pos); + } + x => { + eprintln!("parse failed: {:?} ({})", path, x); + let mut err = x.source(); + while let Some(x) = err { + eprintln!(" Caused by {}", x); + err = x.source(); + } + } + } + exit_code = 1; + } } } } - + // return exit code exit_code } @@ -126,8 +189,8 @@ static CHAR_CR: u8 = 0x0d; static CHAR_LF: u8 = 0x0a; fn print_parse_error( - origin_path: &PathBuf, - origin_pos: &usize + origin_path: &PathBuf, + origin_pos: &usize ) { let mut f = File::open(&origin_path).unwrap(); let mut s = String::new(); @@ -187,18 +250,18 @@ fn print_parse_error( } fn show_macro_defs( - defines: &HashMap> + defines: &HashMap> ) { - for (_, value) in defines.into_iter() { - match value { - Some(define) => println!(" - '{:?}'", define), - _ => (), - } + for (_, value) in defines.into_iter() { + match value { + Some(define) => println!(" - '{:?}'", define), + _ => (), } + } } fn analyze_defs( - syntax_tree: &SyntaxTree + syntax_tree: &SyntaxTree ) { // &SyntaxTree is iterable for node in syntax_tree { @@ -206,198 +269,260 @@ fn analyze_defs( match node { RefNode::ModuleDeclarationNonansi(x) => { // unwrap_node! gets the nearest ModuleIdentifier from x - let id = match unwrap_node!(x, ModuleIdentifier) { - None => { continue; }, - Some(x) => x - }; - let id = match get_identifier(id) { - None => { continue; }, - Some(x) => x - }; + let id = match unwrap_node!(x, ModuleIdentifier) { + None => { continue; }, + Some(x) => x + }; + let id = match get_identifier(id) { + None => { continue; }, + Some(x) => x + }; // Original string can be got by SyntaxTree::get_str(self, node: &RefNode) let id = match syntax_tree.get_str(&id) { - None => { continue; }, - Some(x) => x - }; + None => { continue; }, + Some(x) => x + }; // Declare the new module - println!(" - mod_name: {}", escape_str(id)); - println!(" insts:"); + println!(" - mod_name: {}", escape_str(id)); + println!(" insts:"); } RefNode::ModuleDeclarationAnsi(x) => { - let id = match unwrap_node!(x, ModuleIdentifier) { - None => { continue; }, - Some(x) => x - }; - let id = match get_identifier(id) { - None => { continue; }, - Some(x) => x - }; + let id = match unwrap_node!(x, ModuleIdentifier) { + None => { continue; }, + Some(x) => x + }; + let id = match get_identifier(id) { + None => { continue; }, + Some(x) => x + }; let id = match syntax_tree.get_str(&id) { - None => { continue; }, - Some(x) => x - }; - println!(" - mod_name: {}", escape_str(id)); - println!(" insts:"); + None => { continue; }, + Some(x) => x + }; + println!(" - mod_name: {}", escape_str(id)); + println!(" insts:"); } RefNode::PackageDeclaration(x) => { - let id = match unwrap_node!(x, PackageIdentifier) { - None => { continue; }, - Some(x) => x - }; - let id = match get_identifier(id) { - None => { continue; }, - Some(x) => x - }; + let id = match unwrap_node!(x, PackageIdentifier) { + None => { continue; }, + Some(x) => x + }; + let id = match get_identifier(id) { + None => { continue; }, + Some(x) => x + }; let id = match syntax_tree.get_str(&id) { - None => { continue; }, - Some(x) => x - }; - println!(" - pkg_name: {}", escape_str(id)); - println!(" insts:"); + None => { continue; }, + Some(x) => x + }; + println!(" - pkg_name: {}", escape_str(id)); + println!(" insts:"); } RefNode::InterfaceDeclaration(x) => { - let id = match unwrap_node!(x, InterfaceIdentifier) { - None => { continue; }, - Some(x) => x - }; - let id = match get_identifier(id) { - None => { continue; }, - Some(x) => x - }; + let id = match unwrap_node!(x, InterfaceIdentifier) { + None => { continue; }, + Some(x) => x + }; + let id = match get_identifier(id) { + None => { continue; }, + Some(x) => x + }; let id = match syntax_tree.get_str(&id) { - None => { continue; }, - Some(x) => x - }; - println!(" - intf_name: {}", escape_str(id)); - println!(" insts:"); + None => { continue; }, + Some(x) => x + }; + println!(" - intf_name: {}", escape_str(id)); + println!(" insts:"); } RefNode::ModuleInstantiation(x) => { - // write the module name - let id = match unwrap_node!(x, ModuleIdentifier) { - None => { continue; }, - Some(x) => x - }; - let id = match get_identifier(id) { - None => { continue; }, - Some(x) => x - }; + // write the module name + let id = match unwrap_node!(x, ModuleIdentifier) { + None => { continue; }, + Some(x) => x + }; + let id = match get_identifier(id) { + None => { continue; }, + Some(x) => x + }; let id = match syntax_tree.get_str(&id) { - None => { continue; }, - Some(x) => x - }; + None => { continue; }, + Some(x) => x + }; println!(" - mod_name: {}", escape_str(id)); // write the instance name - let id = match unwrap_node!(x, InstanceIdentifier) { - None => { continue; }, - Some(x) => x - }; - let id = match get_identifier(id) { - None => { continue; }, - Some(x) => x - }; + let id = match unwrap_node!(x, InstanceIdentifier) { + None => { continue; }, + Some(x) => x + }; + let id = match get_identifier(id) { + None => { continue; }, + Some(x) => x + }; let id = match syntax_tree.get_str(&id) { - None => { continue; }, - Some(x) => x - }; + None => { continue; }, + Some(x) => x + }; println!(" inst_name: {}", escape_str(id)); - } + } + RefNode::AnsiPortDeclarationNet(x) => { + // write the module name + let id = match unwrap_node!(x, InterfaceIdentifier) { + None => { continue; }, + Some(x) => x + }; + let id = match get_identifier(id) { + None => { continue; }, + Some(x) => x + }; + let id = match syntax_tree.get_str(&id) { + None => { continue; }, + Some(x) => x + }; + println!(" - intf_name: {}", escape_str(id)); + // write the instance name + let id = match unwrap_node!(x, PortIdentifier) { + None => { continue; }, + Some(x) => x + }; + let id = match get_identifier(id) { + None => { continue; }, + Some(x) => x + }; + let id = match syntax_tree.get_str(&id) { + None => { continue; }, + Some(x) => x + }; + println!(" inst_name: {}", escape_str(id)); + } RefNode::PackageImportItem(x) => { - // write the package name - let id = match unwrap_node!(x, PackageIdentifier) { - None => { continue; }, - Some(x) => x - }; - let id = match get_identifier(id) { - None => { continue; }, - Some(x) => x - }; + // write the package name + let id = match unwrap_node!(x, PackageIdentifier) { + None => { continue; }, + Some(x) => x + }; + let id = match get_identifier(id) { + None => { continue; }, + Some(x) => x + }; + let id = match syntax_tree.get_str(&id) { + None => { continue; }, + Some(x) => x + }; + println!(" - pkg_name: {}", escape_str(id)); + } + RefNode::ClassDeclaration(x) => { + // write the package name + let id = match unwrap_node!(x, ClassIdentifier) { + None => { continue; }, + Some(x) => x + }; + let id = match get_identifier(id) { + None => { continue; }, + Some(x) => x + }; + let id = match syntax_tree.get_str(&id) { + None => { continue; }, + Some(x) => x + }; + println!(" - class_def: {}", escape_str(id)); + } + RefNode::ImplicitClassHandleOrClassScopeOrPackageScope(x) => { + // write the package name + let id = match unwrap_node!(x, ClassIdentifier) { + None => { continue; }, + Some(x) => x + }; + let id = match get_identifier(id) { + None => { continue; }, + Some(x) => x + }; let id = match syntax_tree.get_str(&id) { - None => { continue; }, - Some(x) => x - }; + None => { continue; }, + Some(x) => x + }; println!(" - pkg_name: {}", escape_str(id)); - } - RefNode::ImplicitClassHandleOrClassScope(x) => { - // write the package name - let id = match unwrap_node!(x, ClassIdentifier) { - None => { continue; }, - Some(x) => x - }; - let id = match get_identifier(id) { - None => { continue; }, - Some(x) => x - }; + } + RefNode::PsClassIdentifier(x) => { + // write the package name + let id = match unwrap_node!(x, PackageIdentifier) { + None => { continue; }, + Some(x) => x + }; + let id = match get_identifier(id) { + None => { continue; }, + Some(x) => x + }; let id = match syntax_tree.get_str(&id) { - None => { continue; }, - Some(x) => x - }; + None => { continue; }, + Some(x) => x + }; println!(" - pkg_name: {}", escape_str(id)); - } - RefNode::ImplicitClassHandleOrClassScopeOrPackageScope(x) => { - // write the package name - let id = match unwrap_node!(x, ClassIdentifier) { - None => { continue; }, - Some(x) => x - }; - let id = match get_identifier(id) { - None => { continue; }, - Some(x) => x - }; + } + RefNode::ConstantParamExpression(x) => { + // write the package name + let id = match unwrap_node!(x, ClassIdentifier) { + None => { continue; }, + Some(x) => x + }; + let id = match get_identifier(id) { + None => { continue; }, + Some(x) => x + }; let id = match syntax_tree.get_str(&id) { - None => { continue; }, - Some(x) => x - }; + None => { continue; }, + Some(x) => x + }; println!(" - pkg_name: {}", escape_str(id)); - } + } _ => (), } } } fn print_full_tree( - syntax_tree: &SyntaxTree, - include_whitespace: bool + syntax_tree: &SyntaxTree, + include_whitespace: bool ) { - let mut skip = false; - let mut depth = 3; - for node in syntax_tree.into_iter().event() { - match node { - NodeEvent::Enter(RefNode::Locate(locate)) => { - if !skip { - println!("{}- Token: {}", - " ".repeat(depth), - escape_str(syntax_tree.get_str(locate).unwrap())); - println!("{} Line: {}", - " ".repeat(depth), - locate.line); - } - depth += 1; - } - NodeEvent::Enter(RefNode::WhiteSpace(_)) => { - if !include_whitespace { - skip = true; - } - } - NodeEvent::Leave(RefNode::WhiteSpace(_)) => { - skip = false; - } - NodeEvent::Enter(x) => { - if !skip { - println!("{}- {}:", - " ".repeat(depth), - x); - } - depth += 1; - } - NodeEvent::Leave(_) => { - depth -= 1; - } + let mut skip = false; + let mut depth = 3; + for node in syntax_tree.into_iter().event() { + match node { + NodeEvent::Enter(RefNode::Locate(locate)) => { + if !skip { + println!("{}- Token: {}", + " ".repeat(depth), + escape_str(syntax_tree.get_str(locate).unwrap())); + println!("{} Line: {}", + " ".repeat(depth), + locate.line); + } + depth += 1; + } + NodeEvent::Enter(RefNode::WhiteSpace(_)) => { + if !include_whitespace { + skip = true; } + } + NodeEvent::Leave(RefNode::WhiteSpace(_)) => { + skip = false; + } + NodeEvent::Enter(x) => { + if !skip { + println!("{}- {}:", + " ".repeat(depth), + x); + } + depth += 1; + } + NodeEvent::Leave(_) => { + depth -= 1; + } } + } } fn get_identifier( - node: RefNode + node: RefNode ) -> Option { // unwrap_node! can take multiple types match unwrap_node!(node, SimpleIdentifier, EscapedIdentifier) { @@ -415,7 +540,7 @@ fn get_identifier( // https://github.com/chyh1990/yaml-rust/blob/6cd3ce4abe6894443645c48bdc375808ec911493/src/emitter.rs#L43-L104 fn escape_str(v: &str) -> String { let mut wr = String::new(); - + wr.push_str("\""); let mut start = 0; @@ -474,190 +599,200 @@ fn escape_str(v: &str) -> String { } wr.push_str("\""); - + wr } #[cfg(test)] mod tests { - use super::*; + use super::*; + + fn run_opt_expect(opt: &Opt, val: i32) { + let ret = run_opt(&opt); + assert_eq!(ret, val); + } - fn run_opt_expect(opt: &Opt, val: i32) { - let ret = run_opt(&opt); - assert_eq!(ret, val); - } - fn expect_pass(opt: &Opt) { - run_opt_expect(opt, 0); - } - - fn expect_fail(opt: &Opt) { - run_opt_expect(opt, 1); - } - + run_opt_expect(opt, 0); + } + + fn expect_fail(opt: &Opt) { + run_opt_expect(opt, 1); + } + #[test] fn test_test() { let opt = Opt{ - files: vec![PathBuf::from("testcases/pass/test.sv")], - defines: vec![], - includes: vec![], - full_tree: false, - include_whitespace: false, - ignore_include: false, - separate: false, - show_macro_defs: false, - allow_incomplete: false - }; - expect_pass(&opt); + files: vec![PathBuf::from("testcases/pass/test.sv")], + defines: vec![], + includes: vec![], + full_tree: false, + include_whitespace: false, + ignore_include: false, + separate: false, + show_macro_defs: false, + allow_incomplete: false, + use_stdin: false + }; + expect_pass(&opt); } - + #[test] fn test_broken() { let opt = Opt{ - files: vec![PathBuf::from("testcases/fail/broken.sv")], - defines: vec![], - includes: vec![], - full_tree: false, - include_whitespace: false, - ignore_include: false, - separate: false, - show_macro_defs: false, - allow_incomplete: false - }; - expect_fail(&opt); + files: vec![PathBuf::from("testcases/fail/broken.sv")], + defines: vec![], + includes: vec![], + full_tree: false, + include_whitespace: false, + ignore_include: false, + separate: false, + show_macro_defs: false, + allow_incomplete: false, + use_stdin: false + }; + expect_fail(&opt); } - + #[test] fn test_inc_test() { let opt = Opt{ - files: vec![PathBuf::from("testcases/pass/inc_test.sv")], - defines: vec![], - includes: vec![PathBuf::from("testcases/pass")], - full_tree: false, - include_whitespace: false, - ignore_include: false, - separate: false, - show_macro_defs: false, - allow_incomplete: false - }; - expect_pass(&opt); + files: vec![PathBuf::from("testcases/pass/inc_test.sv")], + defines: vec![], + includes: vec![PathBuf::from("testcases/pass")], + full_tree: false, + include_whitespace: false, + ignore_include: false, + separate: false, + show_macro_defs: false, + allow_incomplete: false, + use_stdin: false + }; + expect_pass(&opt); } - + #[test] fn test_def_test() { let opt = Opt{ - files: vec![PathBuf::from("testcases/pass/def_test.sv")], - defines: vec![String::from("MODULE_NAME=module_name_from_define"), - String::from("EXTRA_INSTANCE")], - includes: vec![], - full_tree: false, - include_whitespace: false, - ignore_include: false, - separate: false, - show_macro_defs: true, - allow_incomplete: false - }; - expect_pass(&opt); + files: vec![PathBuf::from("testcases/pass/def_test.sv")], + defines: vec![String::from("MODULE_NAME=module_name_from_define"), + String::from("EXTRA_INSTANCE")], + includes: vec![], + full_tree: false, + include_whitespace: false, + ignore_include: false, + separate: false, + show_macro_defs: true, + allow_incomplete: false, + use_stdin: false + }; + expect_pass(&opt); } - - #[test] + + #[test] fn test_simple() { let opt = Opt{ - files: vec![PathBuf::from("testcases/pass/simple.sv")], - defines: vec![], - includes: vec![], - full_tree: true, - include_whitespace: false, - ignore_include: false, - separate: false, - show_macro_defs: false, - allow_incomplete: false - }; - expect_pass(&opt); + files: vec![PathBuf::from("testcases/pass/simple.sv")], + defines: vec![], + includes: vec![], + full_tree: true, + include_whitespace: false, + ignore_include: false, + separate: false, + show_macro_defs: false, + allow_incomplete: false, + use_stdin: false + }; + expect_pass(&opt); } - + #[test] fn test_quotes() { let opt = Opt{ - files: vec![PathBuf::from("testcases/pass/quotes.sv")], - defines: vec![], - includes: vec![], - full_tree: true, - include_whitespace: false, - ignore_include: false, - separate: false, - show_macro_defs: false, - allow_incomplete: false - }; - expect_pass(&opt); + files: vec![PathBuf::from("testcases/pass/quotes.sv")], + defines: vec![], + includes: vec![], + full_tree: true, + include_whitespace: false, + ignore_include: false, + separate: false, + show_macro_defs: false, + allow_incomplete: false, + use_stdin: false + }; + expect_pass(&opt); } - + #[test] fn test_pkg() { let opt = Opt{ - files: vec![PathBuf::from("testcases/pass/pkg.sv")], - defines: vec![], - includes: vec![], - full_tree: false, - include_whitespace: false, - ignore_include: false, - separate: false, - show_macro_defs: false, - allow_incomplete: false - }; - expect_pass(&opt); + files: vec![PathBuf::from("testcases/pass/pkg.sv")], + defines: vec![], + includes: vec![], + full_tree: false, + include_whitespace: false, + ignore_include: false, + separate: false, + show_macro_defs: false, + allow_incomplete: false, + use_stdin: false + }; + expect_pass(&opt); } - + #[test] fn test_intf() { let opt = Opt{ - files: vec![PathBuf::from("testcases/pass/intf.sv")], - defines: vec![], - includes: vec![], - full_tree: false, - include_whitespace: false, - ignore_include: false, - separate: false, - show_macro_defs: false, - allow_incomplete: false - }; - expect_pass(&opt); + files: vec![PathBuf::from("testcases/pass/intf.sv")], + defines: vec![], + includes: vec![], + full_tree: false, + include_whitespace: false, + ignore_include: false, + separate: false, + show_macro_defs: false, + allow_incomplete: false, + use_stdin: false + }; + expect_pass(&opt); } - + #[test] fn test_class() { let opt = Opt{ - files: vec![PathBuf::from("testcases/pass/class.sv")], - defines: vec![], - includes: vec![], - full_tree: false, - include_whitespace: false, - ignore_include: false, - separate: false, - show_macro_defs: false, - allow_incomplete: false - }; - expect_pass(&opt); + files: vec![PathBuf::from("testcases/pass/class.sv")], + defines: vec![], + includes: vec![], + full_tree: false, + include_whitespace: false, + ignore_include: false, + separate: false, + show_macro_defs: false, + allow_incomplete: false, + use_stdin: false + }; + expect_pass(&opt); } #[test] fn test_multi() { let opt = Opt{ - files: vec![ - PathBuf::from("testcases/pass/multi/define1.v"), - PathBuf::from("testcases/pass/multi/test1.sv"), - PathBuf::from("testcases/pass/multi/define2.v"), - PathBuf::from("testcases/pass/multi/dut.v") - ], - defines: vec![], - includes: vec![], - full_tree: false, - include_whitespace: false, - ignore_include: false, - separate: false, - show_macro_defs: false, - allow_incomplete: false - }; - expect_pass(&opt); + files: vec![ + PathBuf::from("testcases/pass/multi/define1.v"), + PathBuf::from("testcases/pass/multi/test1.sv"), + PathBuf::from("testcases/pass/multi/define2.v"), + PathBuf::from("testcases/pass/multi/dut.v") + ], + defines: vec![], + includes: vec![], + full_tree: false, + include_whitespace: false, + ignore_include: false, + separate: false, + show_macro_defs: false, + allow_incomplete: false, + use_stdin: false + }; + expect_pass(&opt); } }