176 lines
5.4 KiB
Rust
176 lines
5.4 KiB
Rust
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<PathBuf>,
|
|
|
|
/// Include path
|
|
#[structopt(short = "i", long = "include", multiple = true, number_of_values = 1)]
|
|
pub includes: Vec<PathBuf>,
|
|
|
|
/// 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<String>,
|
|
|
|
/// 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)
|
|
);
|
|
}
|
|
}
|
|
}
|