use std::collections::HashMap; use std::error::Error as StdError; use std::fs::File; use std::io::Read; use std::path::PathBuf; use std::{cmp, process}; use structopt::StructOpt; use sv_parser::{parse_sv, Define, DefineText}; use sv_parser_error::Error; use sv_parser_pp::preprocess::preprocess; #[derive(StructOpt)] struct Opt { pub files: Vec, /// Include path #[structopt(short = "i", long = "include", multiple = true, number_of_values = 1)] pub includes: Vec, /// Show syntax tree #[structopt(short = "t", long = "tree")] pub tree: bool, /// Show preprocesed text #[structopt(short = "p", long = "pp")] pub pp: bool, /// Allow incomplete source code #[structopt(long = "incomplete")] pub incomplete: bool, /// Define #[structopt(short = "d", long = "define", multiple = true, number_of_values = 1)] pub defines: Vec, /// Quiet #[structopt(short = "q", long = "quiet")] pub quiet: bool, } fn main() { let opt = Opt::from_args(); let mut defines = HashMap::new(); for define in &opt.defines { 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(); Some(DefineText::new(x, None)) } else { None }; let define = Define::new(ident.clone(), vec![], text); defines.insert(ident, Some(define)); } let builder = std::thread::Builder::new().stack_size(20 * 1024 * 1024); let child = builder .spawn(move || { let mut exit = 0; for path in &opt.files { if opt.pp { match preprocess( &path, &defines, &opt.includes, false, // strip_comments false, // ignore_include ) { Ok((preprocessed_text, new_defines)) => { println!("{}", preprocessed_text.text()); defines = new_defines; } _ => (), } } else { match parse_sv(&path, &defines, &opt.includes, false, opt.incomplete) { Ok((syntax_tree, new_defines)) => { if opt.tree { println!("{}", syntax_tree); } defines = new_defines; if !opt.quiet { println!("parse succeeded: {:?}", path); } } Err(x) => { match x { Error::Parse(Some((origin_path, origin_pos))) => { println!("parse failed: {:?}", path); print_parse_error(&origin_path, &origin_pos); } x => { println!("parse failed: {:?} ({:?})", path, x); let mut err = x.source(); while let Some(x) = err { println!(" Caused by {}", x); err = x.source(); } } } exit = 1; } } } } process::exit(exit); }) .expect("thread spawn failure"); let _ = child.join(); } static CHAR_CR: u8 = 0x0d; static CHAR_LF: u8 = 0x0a; fn print_parse_error(origin_path: &PathBuf, origin_pos: &usize) { let mut f = File::open(&origin_path).unwrap(); let mut s = String::new(); let _ = f.read_to_string(&mut s); let mut pos = 0; let mut column = 1; let mut last_lf = None; while pos < s.len() { if s.as_bytes()[pos] == CHAR_LF { column += 1; last_lf = Some(pos); } pos += 1; if *origin_pos == pos { let row = if let Some(last_lf) = last_lf { pos - last_lf } else { pos + 1 }; let mut next_crlf = pos; while next_crlf < s.len() { if s.as_bytes()[next_crlf] == CHAR_CR || s.as_bytes()[next_crlf] == CHAR_LF { break; } next_crlf += 1; } let column_len = format!("{}", column).len(); print!(" {}:{}:{}\n", origin_path.to_string_lossy(), column, row); print!("{}|\n", " ".repeat(column_len + 1)); print!("{} |", column); let beg = if let Some(last_lf) = last_lf { last_lf + 1 } else { 0 }; print!( " {}\n", String::from_utf8_lossy(&s.as_bytes()[beg..next_crlf]) ); print!("{}|", " ".repeat(column_len + 1)); print!( " {}{}\n", " ".repeat(pos - beg), "^".repeat(cmp::min(origin_pos + 1, next_crlf) - origin_pos) ); } } }