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_sabre::data_expr_to_term;
15
use merc_sabre::to_rewrite_spec;
16
use merc_syntax::DataExpr;
17
use merc_syntax::UntypedDataSpecification;
18
use merc_tools::VerbosityFlag;
19
use merc_tools::Version;
20
use merc_tools::VersionFlag;
21
use merc_unsafety::print_allocator_metrics;
22
use merc_utilities::MercError;
23
use merc_utilities::Timing;
24

            
25
mod trs_format;
26

            
27
pub use trs_format::*;
28

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

            
36
    #[command(flatten)]
37
    verbosity: VerbosityFlag,
38

            
39
    #[command(subcommand)]
40
    commands: Option<Commands>,
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
49
    Convert(ConvertArgs),
50
}
51

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

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

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

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

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

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

            
78
fn main() -> Result<ExitCode, MercError> {
79
    let cli = Cli::parse();
80

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

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

            
91
    let timing = Timing::new();
92

            
93
    if let Some(command) = cli.commands {
94
        match command {
95
            Commands::Rewrite(args) => {
96
                if args.specification.extension() == Some(OsStr::new("rec")) {
97
                    if args.terms.is_some() {
98
                        warn!(
99
                            "The --terms option is currently ignored when rewriting REC specifications, the terms are taken from the REC spec."
100
                        );
101
                    }
102

            
103
                    let (syntax_spec, syntax_terms) = load_rec_from_file(&args.specification)?;
104

            
105
                    let spec = syntax_spec.to_rewrite_spec();
106

            
107
                    rewrite_rec(args.rewriter, &spec, &syntax_terms, args.output, &timing)?;
108
                } else if args.specification.extension() == Some(OsStr::new("mcrl2")) {
109
                    let spec_text = std::fs::read_to_string(&args.specification)?;
110
                    let spec = UntypedDataSpecification::parse(&spec_text)?;
111

            
112
                    if let Some(term) = args.terms {
113
                        let data_expr = DataExpr::parse(&term)?;
114
                        let term = data_expr_to_term(&data_expr);
115

            
116
                        let rewrite_spec = to_rewrite_spec(&spec)?;
117
                        rewrite_rec(args.rewriter, &rewrite_spec, &[term.into()], args.output, &timing)?;
118
                    } else {
119
                        return Err("No terms provided for rewriting mCRL2 specification".into());
120
                    }
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
    timing.print();
139

            
140
    print_allocator_metrics();
141
    Ok(ExitCode::SUCCESS)
142
}