Last active
November 26, 2019 06:13
-
-
Save jyn514/ea4c4bd0d39a7634ed5ce71a55a67385 to your computer and use it in GitHub Desktop.
Convert a bunch of string literals to files. Used for https://github.com/jyn514/rcc/tree/external-tests
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extern crate syn; | |
use syn::Expr; | |
use std::fs; | |
use std::path::Path; | |
const TARGET_DIR: &str = "runner-tests"; | |
fn main() { | |
let mut args = std::env::args(); | |
args.next(); // discard exe name | |
fs::create_dir_all(TARGET_DIR).expect("could not create target directory"); | |
for file in args { | |
let file = Path::new(&file); | |
if !file.exists() { | |
println!("warning: file {} does not exist, skipping", file.display()); | |
continue; | |
} | |
convert_file(&file); | |
} | |
} | |
fn convert_file(file: &Path) { | |
println!("converting file {}", file.display()); | |
let code = fs::read_to_string(file).expect("could not read file"); | |
let parsed = syn::parse_file(&code).expect("not valid rust"); | |
let base = Path::new(TARGET_DIR).join( | |
file.file_stem() | |
.expect("must be run on a file, not a directory"), | |
); | |
fs::create_dir_all(&base).expect("could not create directory"); | |
for item in parsed.items { | |
if let syn::Item::Fn(func) = item { | |
convert_func(func, &base); | |
} | |
} | |
} | |
fn convert_func(func: syn::ItemFn, file_path: &Path) { | |
use syn::Stmt; | |
let mut calls: Vec<_> = func | |
.block | |
.stmts | |
.iter() | |
.filter_map(|stmt| match stmt { | |
Stmt::Expr(Expr::Call(call)) | Stmt::Semi(Expr::Call(call), _) => Some(call), | |
_ => None, | |
}) | |
.map(|call| { | |
let name = if let Expr::Path(utils_path) = &*call.func { | |
utils_path.path.segments.last().unwrap().ident.to_string() | |
} else { | |
panic!("indirection not supported"); | |
}; | |
(name, call.args.iter().collect()) | |
}) | |
.collect(); | |
let func_name = func.sig.ident.to_string(); | |
let target_root = file_path.join(func_name); | |
match calls.len() { | |
0 => return, | |
1 => { | |
let call = calls.pop().unwrap(); | |
let target = target_root.to_path_buf().with_extension("c"); | |
convert_call(&call.0, call.1, &target); | |
} | |
_ => convert_multiple(&target_root, calls), | |
} | |
} | |
fn convert_multiple(target_file: &Path, calls: Vec<(String, Vec<&Expr>)>) { | |
fs::create_dir_all(&target_file).expect("could not create directory"); | |
for (i, call) in calls.into_iter().enumerate() { | |
let dest = target_file.join(i.to_string()).with_extension("c"); | |
convert_call(&call.0, call.1, &dest); | |
} | |
} | |
fn convert_call(name: &str, mut args: Vec<&Expr>, path: &Path) { | |
use std::io::Write; | |
use syn::{ExprLit, Lit}; | |
let mut fd = fs::File::create(path) | |
.unwrap_or_else(|_| panic!("could not create file {}", path.display())); | |
let tmp_str; | |
let comment = match name { | |
"assert_compiles" => "compile", | |
"assert_compiles_no_main" => "no-main", | |
"assert_compile_error" => "fail", | |
"assert_succeeds" => "succeeds", | |
"assert_crash" => "crash", | |
"assert_code" => { | |
tmp_str = format!( | |
"code: {}", | |
match args.pop().unwrap() { | |
Expr::Lit(ExprLit { | |
lit: Lit::Int(i), .. | |
}) => i.base10_digits(), | |
_ => { | |
println!("warning: {}: second argument for `assert_code` should be a number, skipping", path.display()); | |
return; | |
} | |
} | |
); | |
&tmp_str | |
} | |
"assert_output" => { | |
tmp_str = format!( | |
"output: {}", | |
match args.pop().unwrap() { | |
Expr::Lit(ExprLit { | |
lit: Lit::Str(output), | |
.. | |
}) => output.value(), | |
_ => { | |
println!( | |
"warning: {}: second argument for `assert_output` should be a str", | |
path.display() | |
); | |
return; | |
} | |
} | |
); | |
&tmp_str | |
} | |
"assert_num_errs" => { | |
tmp_str = format!( | |
"errors: {}", | |
match args.pop().unwrap() { | |
Expr::Lit(ExprLit { | |
lit: Lit::Int(i), .. | |
}) => i.base10_digits(), | |
_ => { | |
println!( | |
"warning: {}:, second argument for `assert_num_errs` should be an int", | |
path.display() | |
); | |
return; | |
} | |
} | |
); | |
&tmp_str | |
} | |
_ => { | |
println!( | |
"warning: {}: unrecognized func call {}", | |
path.display(), | |
name | |
); | |
return; | |
} | |
}; | |
let test_program = match args.pop().unwrap() { | |
Expr::Lit(ExprLit { | |
lit: Lit::Str(program), | |
.. | |
}) => program.value(), | |
_ => { | |
println!( | |
"warning: {}: first argument should be a string literal; skipping", | |
path.display() | |
); | |
return; | |
} | |
}; | |
write!(fd, "// {}\n{}", comment, test_program).expect("failed to write to file"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment