1
use std::ffi::OsStr;
2
use std::fs::File;
3
use std::io::Write;
4
use std::path::PathBuf;
5
use std::process::ExitCode;
6

            
7
use clap::Parser;
8
use clap::Subcommand;
9
use log::warn;
10

            
11
use merc_rec_tests::load_rec_from_file;
12
use merc_rewrite::Rewriter;
13
use merc_rewrite::rewrite_rec;
14
use merc_tools::VerbosityFlag;
15
use merc_tools::Version;
16
use merc_tools::VersionFlag;
17
use merc_tools::report_error;
18
use merc_unsafety::print_allocator_metrics;
19
use merc_utilities::MercError;
20
use merc_utilities::Timing;
21

            
22
mod trs_format;
23

            
24
pub use trs_format::*;
25

            
26
/// A command line rewriting tool
27
#[derive(clap::Parser, Debug)]
28
#[command(arg_required_else_help = true)]
29
struct Cli {
30
    #[command(flatten)]
31
    version: VersionFlag,
32

            
33
    #[command(flatten)]
34
    verbosity: VerbosityFlag,
35

            
36
    #[command(subcommand)]
37
    commands: Option<Commands>,
38

            
39
    #[arg(long, global = true)]
40
    timings: bool,
41
}
42

            
43
#[derive(Debug, Subcommand)]
44
enum Commands {
45
    /// Rewrite a term using the rewrite rules specified in a REC file or mCRL2 specification
46
    Rewrite(RewriteArgs),
47

            
48
    /// Convert a REC specification to the TRS format, which is the format used
49
    /// by the term rewrite system termination checking tool called AProVE.
50
    Convert(ConvertArgs),
51
}
52

            
53
#[derive(clap::Args, Debug)]
54
struct RewriteArgs {
55
    rewriter: Rewriter,
56

            
57
    /// The REC specification that contains the rewrite rules.
58
    #[arg(value_name = "SPEC")]
59
    specification: PathBuf,
60

            
61
    /// File containing the terms to be rewritten.
62
    terms: Option<String>,
63

            
64
    /// Print the rewritten term(s)
65
    #[arg(long)]
66
    output: bool,
67
}
68

            
69
#[derive(clap::Args, Debug)]
70
struct ConvertArgs {
71
    /// The REC specification that contains the rewrite rules.
72
    #[arg(value_name = "SPEC")]
73
    specification: PathBuf,
74

            
75
    /// The output file to write the TRS to.
76
    output: String,
77
}
78

            
79
fn main() -> ExitCode {
80
    let cli = Cli::parse();
81

            
82
    env_logger::Builder::new()
83
        .filter_level(cli.verbosity.log_level_filter())
84
        .parse_default_env()
85
        .init();
86

            
87
    if cli.version.into() {
88
        eprintln!("{}", Version);
89
        return ExitCode::SUCCESS;
90
    }
91

            
92
    let timing = Timing::new();
93
    let result = handle_command(cli.commands, &timing);
94

            
95
    if cli.timings {
96
        timing.print();
97
    }
98

            
99
    print_allocator_metrics();
100
    report_error(result)
101
}
102

            
103
fn handle_command(commands: Option<Commands>, timing: &Timing) -> Result<(), MercError> {
104
    if let Some(command) = commands {
105
        match command {
106
            Commands::Rewrite(args) => {
107
                if args.specification.extension() == Some(OsStr::new("rec")) {
108
                    if args.terms.is_some() {
109
                        warn!(
110
                            "The --terms option is currently ignored when rewriting REC specifications, the terms are taken from the REC spec."
111
                        );
112
                    }
113

            
114
                    let (syntax_spec, syntax_terms) = load_rec_from_file(&args.specification)?;
115

            
116
                    let spec = syntax_spec.to_rewrite_spec();
117

            
118
                    rewrite_rec(args.rewriter, &spec, &syntax_terms, args.output, timing)?;
119
                } else if args.specification.extension() == Some(OsStr::new("mcrl2")) {
120
                    return Err("Rewriting mCRL2 specifications is not yet supported".into());
121
                } else {
122
                    return Err("Unsupported file extension for rewriting, expected .rec or .mcrl2".into());
123
                }
124
            }
125
            Commands::Convert(args) => {
126
                if args.specification.extension() == Some(OsStr::new("rec")) {
127
                    // Read the data specification
128
                    let (spec_text, _) = load_rec_from_file(&args.specification)?;
129
                    let spec = spec_text.to_rewrite_spec();
130

            
131
                    let mut output = File::create(args.output)?;
132
                    write!(output, "{}", TrsFormatter::new(&spec))?;
133
                }
134
            }
135
        }
136
    }
137

            
138
    Ok(())
139
}