nandi/rustnimpublic Fork 0
b0ccd80a849033974bea0c5eccff995ecb632c3e
Commits
Clone
git clone https://git.rickub.com/nandi/rustnim.git
git clone ssh://git@rickub.com/nandi/rustnim.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Add enums, Option/Result, `?`, and the machinery base16ct needs around them b0ccd80 · on b0ccd80a849033974bea0c5eccff995ecb632c3e · nandithebull · 11h ago
main.rs · 90 lines · 3.3 KBRust Blame HistoryRaw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
//! rustnim — Rust -> Nim transpiler.
//!
//! Usage: rustnim <input.rs> [-o <output.nim>]

mod fmt;
mod lower;
mod ty;

use std::path::PathBuf;
use std::process::ExitCode;

fn main() -> ExitCode {
    match run() {
        Ok(()) => ExitCode::SUCCESS,
        Err(e) => {
            eprintln!("rustnim: {e}");
            // Exiting non-zero on any failure is load-bearing: the entire
            // point of this project is that a transpiler must never report
            // success for work it did not do.
            ExitCode::FAILURE
        }
    }
}

fn run() -> Result<(), String> {
    let mut args = std::env::args_os().skip(1);
    let mut inputs: Vec<PathBuf> = Vec::new();
    let mut output: Option<PathBuf> = None;
    let mut features: Vec<String> = Vec::new();

    while let Some(a) = args.next() {
        match a.to_string_lossy().as_ref() {
            "-o" | "--output" => {
                output = Some(
                    args.next()
                        .ok_or("`-o` needs a path")?
                        .into(),
                );
            }
            "--cfg" => {
                let v = args.next().ok_or("`--cfg` needs an argument")?;
                let v = v.to_string_lossy().into_owned();
                let f = v
                    .strip_prefix("feature=")
                    .ok_or("only `--cfg feature=<name>` is supported")?;
                features.push(f.trim_matches('"').to_string());
            }
            "-h" | "--help" => {
                println!("usage: rustnim <input.rs>... [--cfg feature=<name>]... [-o <output.nim>]");
                println!();
                println!("Several inputs are concatenated into one Nim module, in the");
                println!("order given. That is how a multi-file crate is handled: Nim");
                println!("has no equivalent of Rust's per-file `mod`, so the items are");
                println!("flattened. Names must not collide across the files.");
                return Ok(());
            }
            s if s.starts_with('-') => return Err(format!("unknown flag `{s}`")),
            _ => inputs.push(a.into()),
        }
    }

    if inputs.is_empty() {
        return Err("no input file; usage: rustnim <input.rs>... [-o <output.nim>]".into());
    }

    // Several files become one Nim module: Rust's `mod` has no Nim analogue
    // inside a single output file, so the items are flattened in argument
    // order. A name collision between two files is a Nim compile error, which
    // is a loud failure rather than a silently shadowed definition.
    let mut items = Vec::new();
    for input in &inputs {
        let src = std::fs::read_to_string(input)
            .map_err(|e| format!("cannot read {}: {e}", input.display()))?;
        let parsed: syn::File = syn::parse_file(&src)
            .map_err(|e| format!("{}: parse error: {e}", input.display()))?;
        items.extend(parsed.items);
    }
    let file = syn::File { shebang: None, frontmatter: None, attrs: Vec::new(), items };

    let mut lowerer = lower::Lowerer::new();
    lowerer.features = features;
    let nim = lowerer.lower_file(&file)?;

    match output {
        Some(p) => std::fs::write(&p, nim)
            .map_err(|e| format!("cannot write {}: {e}", p.display()))?,
        None => print!("{nim}"),
    }
    Ok(())
}