Skip to content

Instantly share code, notes, and snippets.

@jyn514
Last active November 26, 2019 06:13
Show Gist options
  • Save jyn514/ea4c4bd0d39a7634ed5ce71a55a67385 to your computer and use it in GitHub Desktop.
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
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