| Add the differential test runner, and a lowering to measure with it 8ac32af nandithebull 11h ago | 1 | //! rustnim — Rust -> Nim transpiler. |
| 2 | //! |
| 3 | //! Usage: rustnim <input.rs> [-o <output.nim>] |
| 4 | |
| 5 | mod fmt; |
| Lower `bitflags!` directly, checked against the real crate af6e50f nandithebull 8h ago | 6 | mod macros; |
| Expand `macro_rules!` rather than translating it to a Nim template 04eb29f nandithebull 7h ago | 7 | mod mrules; |
| Add the differential test runner, and a lowering to measure with it 8ac32af nandithebull 11h ago | 8 | mod lower; |
| Settle signed-shr and unsigned-wrap semantics against both compilers 87c9cc8 nandi 12h ago | 9 | mod ty; |
| Add the differential test runner, and a lowering to measure with it 8ac32af nandithebull 11h ago | 10 | |
| 11 | use std::path::PathBuf; |
| 12 | use std::process::ExitCode; |
| 13 | |
| 14 | fn main() -> ExitCode { |
| 15 | match run() { |
| 16 | Ok(()) => ExitCode::SUCCESS, |
| 17 | Err(e) => { |
| 18 | eprintln!("rustnim: {e}"); |
| 19 | // Exiting non-zero on any failure is load-bearing: the entire |
| 20 | // point of this project is that a transpiler must never report |
| 21 | // success for work it did not do. |
| 22 | ExitCode::FAILURE |
| 23 | } |
| 24 | } |
| 25 | } |
| 26 | |
| 27 | fn run() -> Result<(), String> { |
| 28 | let mut args = std::env::args_os().skip(1); |
| Add enums, Option/Result, `?`, and the machinery base16ct needs around them b0ccd80 nandithebull 11h ago | 29 | let mut inputs: Vec<PathBuf> = Vec::new(); |
| Add the differential test runner, and a lowering to measure with it 8ac32af nandithebull 11h ago | 30 | let mut output: Option<PathBuf> = None; |
| Add enums, Option/Result, `?`, and the machinery base16ct needs around them b0ccd80 nandithebull 11h ago | 31 | let mut features: Vec<String> = Vec::new(); |
| Add the differential test runner, and a lowering to measure with it 8ac32af nandithebull 11h ago | 32 | |
| 33 | while let Some(a) = args.next() { |
| 34 | match a.to_string_lossy().as_ref() { |
| 35 | "-o" | "--output" => { |
| 36 | output = Some( |
| 37 | args.next() |
| 38 | .ok_or("`-o` needs a path")? |
| 39 | .into(), |
| 40 | ); |
| 41 | } |
| Add enums, Option/Result, `?`, and the machinery base16ct needs around them b0ccd80 nandithebull 11h ago | 42 | "--cfg" => { |
| 43 | let v = args.next().ok_or("`--cfg` needs an argument")?; |
| 44 | let v = v.to_string_lossy().into_owned(); |
| 45 | let f = v |
| 46 | .strip_prefix("feature=") |
| 47 | .ok_or("only `--cfg feature=<name>` is supported")?; |
| 48 | features.push(f.trim_matches('"').to_string()); |
| 49 | } |
| Add the differential test runner, and a lowering to measure with it 8ac32af nandithebull 11h ago | 50 | "-h" | "--help" => { |
| Add enums, Option/Result, `?`, and the machinery base16ct needs around them b0ccd80 nandithebull 11h ago | 51 | println!("usage: rustnim <input.rs>... [--cfg feature=<name>]... [-o <output.nim>]"); |
| 52 | println!(); |
| 53 | println!("Several inputs are concatenated into one Nim module, in the"); |
| 54 | println!("order given. That is how a multi-file crate is handled: Nim"); |
| 55 | println!("has no equivalent of Rust's per-file `mod`, so the items are"); |
| 56 | println!("flattened. Names must not collide across the files."); |
| Add the differential test runner, and a lowering to measure with it 8ac32af nandithebull 11h ago | 57 | return Ok(()); |
| 58 | } |
| 59 | s if s.starts_with('-') => return Err(format!("unknown flag `{s}`")), |
| Add enums, Option/Result, `?`, and the machinery base16ct needs around them b0ccd80 nandithebull 11h ago | 60 | _ => inputs.push(a.into()), |
| Add the differential test runner, and a lowering to measure with it 8ac32af nandithebull 11h ago | 61 | } |
| 62 | } |
| 63 | |
| Add enums, Option/Result, `?`, and the machinery base16ct needs around them b0ccd80 nandithebull 11h ago | 64 | if inputs.is_empty() { |
| 65 | return Err("no input file; usage: rustnim <input.rs>... [-o <output.nim>]".into()); |
| 66 | } |
| Add the differential test runner, and a lowering to measure with it 8ac32af nandithebull 11h ago | 67 | |
| Add enums, Option/Result, `?`, and the machinery base16ct needs around them b0ccd80 nandithebull 11h ago | 68 | // Several files become one Nim module: Rust's `mod` has no Nim analogue |
| 69 | // inside a single output file, so the items are flattened in argument |
| 70 | // order. A name collision between two files is a Nim compile error, which |
| 71 | // is a loud failure rather than a silently shadowed definition. |
| Add closures and unsafe; base16ct's lower.rs and upper.rs go through 0a375d8 nandithebull 10h ago | 72 | // The first input is the crate root; each later one is a module named by |
| 73 | // its file stem. That is what keeps `lower::decode` and `mixed::decode` |
| 74 | // apart once everything is flattened into a single Nim module. |
| 75 | let mut files = Vec::new(); |
| 76 | for (i, input) in inputs.iter().enumerate() { |
| Add enums, Option/Result, `?`, and the machinery base16ct needs around them b0ccd80 nandithebull 11h ago | 77 | let src = std::fs::read_to_string(input) |
| 78 | .map_err(|e| format!("cannot read {}: {e}", input.display()))?; |
| 79 | let parsed: syn::File = syn::parse_file(&src) |
| 80 | .map_err(|e| format!("{}: parse error: {e}", input.display()))?; |
| Add closures and unsafe; base16ct's lower.rs and upper.rs go through 0a375d8 nandithebull 10h ago | 81 | let module = if i == 0 { |
| 82 | String::new() |
| 83 | } else { |
| 84 | input |
| 85 | .file_stem() |
| 86 | .map(|s| s.to_string_lossy().into_owned()) |
| 87 | .unwrap_or_default() |
| 88 | }; |
| 89 | files.push((module, parsed)); |
| Add enums, Option/Result, `?`, and the machinery base16ct needs around them b0ccd80 nandithebull 11h ago | 90 | } |
| Add the differential test runner, and a lowering to measure with it 8ac32af nandithebull 11h ago | 91 | |
| Add enums, Option/Result, `?`, and the machinery base16ct needs around them b0ccd80 nandithebull 11h ago | 92 | let mut lowerer = lower::Lowerer::new(); |
| 93 | lowerer.features = features; |
| Add trait impls and slice iterators; base16ct's decoder now goes through ae9f986 nandithebull 10h ago | 94 | // A `mod x;` is satisfied when x.rs is one of the inputs. |
| 95 | lowerer.modules = inputs |
| 96 | .iter() |
| 97 | .filter_map(|p| p.file_stem().map(|s| s.to_string_lossy().into_owned())) |
| 98 | .collect(); |
| Add closures and unsafe; base16ct's lower.rs and upper.rs go through 0a375d8 nandithebull 10h ago | 99 | let nim = lowerer.lower_file(&files)?; |
| Add the differential test runner, and a lowering to measure with it 8ac32af nandithebull 11h ago | 100 | |
| 101 | match output { |
| 102 | Some(p) => std::fs::write(&p, nim) |
| 103 | .map_err(|e| format!("cannot write {}: {e}", p.display()))?, |
| 104 | None => print!("{nim}"), |
| 105 | } |
| 106 | Ok(()) |
| 107 | } |