1use std::{ffi::OsString, path::PathBuf, rc::Rc, sync::Arc};
2
3use clap::{Parser, Subcommand};
4use log::Log;
5use midenc_compile as compile;
6#[cfg(feature = "debug")]
7use midenc_debug as debugger;
8use midenc_hir::Context;
9use midenc_session::{
10 diagnostics::{Emitter, Report},
11 InputFile,
12};
13
14use crate::ClapDiagnostic;
15
16#[derive(Debug, Parser)]
18#[command(name = "midenc")]
19#[command(author, version, about = "A compiler for Miden Assembly", long_about = None)]
20pub struct Midenc {
21 #[command(subcommand)]
22 command: Commands,
23}
24
25#[derive(Debug, Subcommand)]
26enum Commands {
27 Compile {
28 #[arg(required(true), value_name = "FILE")]
32 input: InputFile,
33 #[command(flatten)]
34 options: compile::Compiler,
35 },
36 #[cfg(feature = "debug")]
38 Run {
39 #[arg(required(true), value_name = "FILE")]
45 input: InputFile,
46 #[arg(long, value_name = "FILE")]
59 inputs: Option<debugger::DebuggerConfig>,
60 #[arg(long, short = 'n', default_value_t = 16)]
62 num_outputs: usize,
63 #[arg(last(true), value_name = "ARGV")]
73 args: Vec<debugger::Felt>,
74 #[command(flatten)]
75 options: debugger::Debugger,
76 },
77 #[cfg(feature = "debug")]
81 Debug {
82 #[arg(required(true), value_name = "FILE")]
88 input: InputFile,
89 #[arg(long, value_name = "FILE")]
95 inputs: Option<debugger::DebuggerConfig>,
96 #[arg(last(true), value_name = "ARGV")]
106 args: Vec<debugger::Felt>,
107 #[command(flatten)]
108 options: debugger::Debugger,
109 },
110}
111
112impl Midenc {
113 pub fn run<P, A>(
114 cwd: P,
115 args: A,
116 logger: Box<dyn Log>,
117 filter: log::LevelFilter,
118 ) -> Result<(), Report>
119 where
120 P: Into<PathBuf>,
121 A: IntoIterator<Item = OsString>,
122 {
123 Self::run_with_emitter(cwd, args, None, logger, filter)
124 }
125
126 pub fn run_with_emitter<P, A>(
127 cwd: P,
128 args: A,
129 emitter: Option<Arc<dyn Emitter>>,
130 logger: Box<dyn Log>,
131 filter: log::LevelFilter,
132 ) -> Result<(), Report>
133 where
134 P: Into<PathBuf>,
135 A: IntoIterator<Item = OsString>,
136 {
137 let command = <Self as clap::CommandFactory>::command();
138 let command = command.mut_subcommand("compile", midenc_session::flags::register_flags);
139
140 let mut matches = command.try_get_matches_from(args).map_err(ClapDiagnostic::from)?;
141 let compile_matches = matches.subcommand_matches("compile").cloned().unwrap_or_default();
142 let cli = <Self as clap::FromArgMatches>::from_arg_matches_mut(&mut matches)
143 .map_err(format_error::<Self>)
144 .map_err(ClapDiagnostic::from)?;
145
146 cli.invoke(cwd.into(), emitter, logger, filter, compile_matches)
147 }
148
149 fn invoke(
150 self,
151 cwd: PathBuf,
152 emitter: Option<Arc<dyn Emitter>>,
153 logger: Box<dyn Log>,
154 filter: log::LevelFilter,
155 matches: clap::ArgMatches,
156 ) -> Result<(), Report> {
157 match self.command {
158 Commands::Compile { input, mut options } => {
159 log::set_boxed_logger(logger)
160 .unwrap_or_else(|err| panic!("failed to install logger: {err}"));
161 log::set_max_level(filter);
162 if options.working_dir.is_none() {
163 options.working_dir = Some(cwd);
164 }
165 let session = Rc::new(
166 options.into_session(vec![input], emitter).with_extra_flags(matches.into()),
167 );
168 let context = Rc::new(Context::new(session));
169 compile::compile(context)
170 }
171 #[cfg(feature = "debug")]
172 Commands::Run {
173 input,
174 inputs,
175 args,
176 num_outputs,
177 mut options,
178 } => {
179 log::set_boxed_logger(logger)
180 .unwrap_or_else(|err| panic!("failed to install logger: {err}"));
181 log::set_max_level(filter);
182 if options.working_dir.is_none() {
183 options.working_dir = Some(cwd);
184 }
185 let session = options.into_session(vec![input], emitter);
186 let args = args.into_iter().map(|felt| felt.0).collect();
187 debugger::run_noninteractively(inputs, args, num_outputs, Rc::new(session))
188 }
189 #[cfg(feature = "debug")]
190 Commands::Debug {
191 input,
192 inputs,
193 args,
194 mut options,
195 } => {
196 if options.working_dir.is_none() {
197 options.working_dir = Some(cwd);
198 }
199 let session = options.into_session(vec![input], emitter);
200 let args = args.into_iter().map(|felt| felt.0).collect();
201 debugger::run(inputs, args, Rc::new(session), logger)
202 }
203 }
204 }
205}
206
207fn format_error<I: clap::CommandFactory>(err: clap::Error) -> clap::Error {
208 let mut cmd = I::command();
209 err.format(&mut cmd)
210}