calyx_opt/traversal/
construct.rs1use super::Visitor;
2use calyx_ir as ir;
3use calyx_utils::{CalyxResult, OutputFile};
4use itertools::Itertools;
5use linked_hash_map::LinkedHashMap;
6
7#[derive(Clone)]
8pub enum ParseVal {
10 Bool(bool),
12 Num(i64),
14 String(String),
16 List(Vec<ParseVal>),
18 OutStream(OutputFile),
20}
21
22impl ParseVal {
23 pub fn bool(&self) -> bool {
24 let ParseVal::Bool(b) = self else {
25 panic!("Expected bool, got {self}");
26 };
27 *b
28 }
29
30 pub fn num(&self) -> i64 {
31 let ParseVal::Num(n) = self else {
32 panic!("Expected number, got {self}");
33 };
34 *n
35 }
36
37 pub fn string(&self) -> String {
38 let ParseVal::String(s) = self else {
39 panic!("Expected String, got {self}");
40 };
41 s.clone()
42 }
43
44 pub fn pos_num(&self) -> Option<u64> {
45 let n = self.num();
46 if n < 0 { None } else { Some(n as u64) }
47 }
48
49 pub fn num_list(&self) -> Vec<i64> {
50 match self {
51 ParseVal::List(l) => {
52 l.iter().map(ParseVal::num).collect::<Vec<_>>()
53 }
54 _ => panic!("Expected list of numbers, got {self}"),
55 }
56 }
57
58 pub fn num_list_exact<const N: usize>(&self) -> [Option<i64>; N] {
61 let list = self.num_list();
62 let len = list.len();
63 if len > N {
64 panic!("Expected list of {N} numbers, got {len}");
65 }
66 list.into_iter()
67 .map(Some)
68 .chain(std::iter::repeat_n(None, N - len))
69 .collect::<Vec<_>>()
70 .try_into()
71 .unwrap()
72 }
73
74 pub fn not_null_outstream(&self) -> Option<OutputFile> {
76 match self {
77 ParseVal::OutStream(o) => {
78 if matches!(o, OutputFile::Null) {
79 None
80 } else {
81 Some(o.clone())
82 }
83 }
84 _ => panic!("Expected output stream, got {self}"),
85 }
86 }
87}
88impl std::fmt::Display for ParseVal {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 match self {
91 ParseVal::Bool(b) => write!(f, "{b}"),
92 ParseVal::Num(n) => write!(f, "{n}"),
93 ParseVal::String(s) => write!(f, "{s}"),
94 ParseVal::List(l) => {
95 write!(f, "[")?;
96 for (i, e) in l.iter().enumerate() {
97 if i != 0 {
98 write!(f, ", ")?;
99 }
100 write!(f, "{e}")?;
101 }
102 write!(f, "]")
103 }
104 ParseVal::OutStream(o) => write!(f, "{o}"),
105 }
106 }
107}
108
109pub struct PassOpt {
111 name: &'static str,
112 description: &'static str,
113 default: ParseVal,
114 parse: fn(&str) -> Option<ParseVal>,
115}
116
117impl PassOpt {
118 pub const fn new(
119 name: &'static str,
120 description: &'static str,
121 default: ParseVal,
122 parse: fn(&str) -> Option<ParseVal>,
123 ) -> Self {
124 Self {
125 name,
126 description,
127 default,
128 parse,
129 }
130 }
131
132 pub const fn name(&self) -> &'static str {
133 self.name
134 }
135
136 pub const fn description(&self) -> &'static str {
137 self.description
138 }
139
140 pub const fn default(&self) -> &ParseVal {
141 &self.default
142 }
143
144 fn parse(&self, s: &str) -> Option<ParseVal> {
145 (self.parse)(s)
146 }
147
148 fn parse_list(
151 s: &str,
152 parse: fn(&str) -> Option<ParseVal>,
153 ) -> Option<ParseVal> {
154 let mut res = Vec::new();
155 for e in s.split(',') {
156 res.push(parse(e)?);
157 }
158 Some(ParseVal::List(res))
159 }
160
161 pub fn parse_bool(s: &str) -> Option<ParseVal> {
162 match s {
163 "true" => Some(ParseVal::Bool(true)),
164 "false" => Some(ParseVal::Bool(false)),
165 _ => None,
166 }
167 }
168
169 pub fn parse_num(s: &str) -> Option<ParseVal> {
171 s.parse::<i64>().ok().map(ParseVal::Num)
172 }
173
174 pub fn parse_string(s: &str) -> Option<ParseVal> {
176 Some(ParseVal::String(s.to_string()))
177 }
178
179 pub fn parse_num_list(s: &str) -> Option<ParseVal> {
181 Self::parse_list(s, Self::parse_num)
182 }
183
184 pub fn parse_outstream(s: &str) -> Option<ParseVal> {
185 s.parse::<OutputFile>().ok().map(ParseVal::OutStream)
186 }
187}
188
189pub trait Named {
195 fn name() -> &'static str;
197 fn description() -> &'static str;
199 fn opts() -> Vec<PassOpt> {
202 vec![]
203 }
204}
205
206pub trait ConstructVisitor {
214 fn get_opts(ctx: &ir::Context) -> LinkedHashMap<&'static str, ParseVal>
215 where
216 Self: Named,
217 {
218 let opts = Self::opts();
219 let n = Self::name();
220 let mut values: LinkedHashMap<&'static str, ParseVal> = ctx
221 .extra_opts
222 .iter()
223 .filter_map(|opt| {
224 let mut splits = opt.split(':');
226 if let Some(pass) = splits.next() {
227 if pass == n {
228 let mut splits = splits.next()?.split('=');
229 let opt = splits.next()?.to_string();
230 let Some(opt) = opts.iter().find(|o| o.name == opt) else {
231 log::warn!("Ignoring unknown option for pass `{n}`: {opt}");
232 return None;
233 };
234 let val = if let Some(v) = splits.next() {
235 let Some(v) = opt.parse(v) else {
236 log::warn!(
237 "Ignoring invalid value for option `{n}:{}`: {v}",
238 opt.name(),
239 );
240 return None;
241 };
242 v
243 } else {
244 ParseVal::Bool(true)
245 };
246 return Some((opt.name(), val));
247 }
248 }
249 None
250 })
251 .collect();
252
253 if log::log_enabled!(log::Level::Debug) {
254 log::debug!(
255 "Extra options for {}: {}",
256 Self::name(),
257 values.iter().map(|(o, v)| format!("{o}->{v}")).join(", ")
258 );
259 }
260
261 for opt in opts {
263 if !values.contains_key(opt.name()) {
264 values.insert(opt.name(), opt.default.clone());
265 }
266 }
267
268 values
269 }
270
271 fn from(_ctx: &ir::Context) -> CalyxResult<Self>
273 where
274 Self: Sized;
275
276 fn clear_data(&mut self);
279}
280
281impl<T: Default + Sized + Visitor> ConstructVisitor for T {
283 fn from(_ctx: &ir::Context) -> CalyxResult<Self> {
284 Ok(T::default())
285 }
286
287 fn clear_data(&mut self) {
288 *self = T::default();
289 }
290}