1use crate::interpreter::value::*;
5use crate::interpreter::{BuiltinContext, DiagnosticStyle};
6use itertools::Itertools;
7use minir_macros::{builtin, interpreter_builtin};
8
9fn named_bool(named: &[(String, RValue)], key: &str, default: bool) -> bool {
13 named
14 .iter()
15 .find(|(k, _)| k == key)
16 .map(|(_, v)| match v {
17 RValue::Vector(rv) => rv.as_logical_scalar().unwrap_or(default),
18 _ => default,
19 })
20 .unwrap_or(default)
21}
22
23#[builtin]
33fn builtin_stop(args: &[RValue], _named: &[(String, RValue)]) -> Result<RValue, RError> {
34 if let Some(first) = args.first() {
36 let classes = get_class(first);
37 if classes.iter().any(|c| c == "condition") {
38 return Err(RError::Condition {
39 condition: first.clone(),
40 kind: ConditionKind::Error,
41 });
42 }
43 }
44 let msg = args
45 .iter()
46 .map(|v| match v {
47 RValue::Vector(vec) => vec.as_character_scalar().unwrap_or_default(),
48 other => format!("{}", other),
49 })
50 .join("");
51 let condition = make_condition(&msg, &["simpleError", "error", "condition"]);
52 Err(RError::Condition {
53 condition,
54 kind: ConditionKind::Error,
55 })
56}
57
58#[interpreter_builtin]
65fn interp_warning(
66 args: &[RValue],
67 _named: &[(String, RValue)],
68 context: &BuiltinContext,
69) -> Result<RValue, RError> {
70 if let Some(first) = args.first() {
72 let classes = get_class(first);
73 if classes.iter().any(|c| c == "condition") {
74 let muffled = context
75 .with_interpreter(|interp| interp.signal_condition(first, &interp.global_env))?;
76 if !muffled {
77 let msg = condition_message_str(first);
79 context.write_err_colored("Warning message:\n", DiagnosticStyle::Warning);
80 context.write_err(&format!("{}\n", msg));
81 }
82 return Ok(RValue::Null);
83 }
84 }
85 let msg = args
86 .iter()
87 .map(|v| match v {
88 RValue::Vector(vec) => vec.as_character_scalar().unwrap_or_default(),
89 other => format!("{}", other),
90 })
91 .join("");
92 let condition = make_condition(&msg, &["simpleWarning", "warning", "condition"]);
93 let muffled = context
94 .with_interpreter(|interp| interp.signal_condition(&condition, &interp.global_env))?;
95 if !muffled {
96 context.write_err_colored("Warning message:\n", DiagnosticStyle::Warning);
97 context.write_err(&format!("{}\n", msg));
98 }
99 Ok(RValue::Null)
100}
101
102#[interpreter_builtin]
109fn interp_message(
110 args: &[RValue],
111 named: &[(String, RValue)],
112 context: &BuiltinContext,
113) -> Result<RValue, RError> {
114 let append_lf = named_bool(named, "appendLF", true);
115
116 if let Some(first) = args.first() {
118 let classes = get_class(first);
119 if classes.iter().any(|c| c == "condition") {
120 let muffled = context
121 .with_interpreter(|interp| interp.signal_condition(first, &interp.global_env))?;
122 if !muffled {
123 let msg = condition_message_str(first);
124 if append_lf {
125 context.write_err_colored(&format!("{}\n", msg), DiagnosticStyle::Message);
126 } else {
127 context.write_err_colored(&msg, DiagnosticStyle::Message);
128 }
129 }
130 return Ok(RValue::Null);
131 }
132 }
133
134 let msg = args
135 .iter()
136 .map(|v| match v {
137 RValue::Vector(vec) => vec.as_character_scalar().unwrap_or_default(),
138 other => format!("{}", other),
139 })
140 .join("");
141 let condition = make_condition(&msg, &["simpleMessage", "message", "condition"]);
142 let muffled = context
143 .with_interpreter(|interp| interp.signal_condition(&condition, &interp.global_env))?;
144 if !muffled {
145 if append_lf {
146 context.write_err_colored(&format!("{}\n", msg), DiagnosticStyle::Message);
147 } else {
148 context.write_err_colored(&msg, DiagnosticStyle::Message);
149 }
150 }
151 Ok(RValue::Null)
152}
153
154#[interpreter_builtin(name = "signalCondition", min_args = 1)]
167fn interp_signal_condition(
168 args: &[RValue],
169 _named: &[(String, RValue)],
170 context: &BuiltinContext,
171) -> Result<RValue, RError> {
172 let condition = args.first().ok_or_else(|| {
173 RError::new(
174 RErrorKind::Argument,
175 "argument 'cond' is missing, with no default".to_string(),
176 )
177 })?;
178 context.with_interpreter(|interp| {
179 interp.signal_condition(condition, &interp.global_env)?;
180 Ok(RValue::Null)
181 })
182}
183
184fn condition_message_str(cond: &RValue) -> String {
188 if let RValue::List(list) = cond {
189 for (name, val) in &list.values {
190 if name.as_deref() == Some("message") {
191 if let RValue::Vector(rv) = val {
192 if let Some(s) = rv.as_character_scalar() {
193 return s;
194 }
195 }
196 }
197 }
198 }
199 String::new()
200}
201
202#[builtin(name = "simpleCondition", min_args = 1)]
209fn builtin_simple_condition(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
210 let msg = args
211 .first()
212 .and_then(|v| v.as_vector().and_then(|rv| rv.as_character_scalar()))
213 .unwrap_or_default();
214 let call = args.get(1).cloned().unwrap_or(RValue::Null);
215 let extra_class = named
216 .iter()
217 .find(|(k, _)| k == "class")
218 .and_then(|(_, v)| v.as_vector().and_then(|rv| rv.as_character_scalar()))
219 .unwrap_or_default();
220 let mut classes: Vec<&str> = Vec::new();
221 if !extra_class.is_empty() {
222 classes.push(&extra_class);
223 }
224 classes.push("condition");
225 let mut list = RList::new(vec![
226 (
227 Some("message".to_string()),
228 RValue::vec(Vector::Character(vec![Some(msg)].into())),
229 ),
230 (Some("call".to_string()), call),
231 ]);
232 let class_vec: Vec<Option<String>> = classes.iter().map(|s| Some(s.to_string())).collect();
233 list.set_attr(
234 "class".to_string(),
235 RValue::vec(Vector::Character(class_vec.into())),
236 );
237 Ok(RValue::List(list))
238}
239
240#[builtin(name = "simpleError", min_args = 1)]
246fn builtin_simple_error(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
247 let msg = args
248 .first()
249 .and_then(|v| v.as_vector().and_then(|rv| rv.as_character_scalar()))
250 .unwrap_or_default();
251 let call = args.get(1).cloned().unwrap_or(RValue::Null);
252 Ok(make_condition_with_call(
253 &msg,
254 call,
255 &["simpleError", "error", "condition"],
256 ))
257}
258
259#[builtin(name = "simpleWarning", min_args = 1)]
265fn builtin_simple_warning(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
266 let msg = args
267 .first()
268 .and_then(|v| v.as_vector().and_then(|rv| rv.as_character_scalar()))
269 .unwrap_or_default();
270 let call = args.get(1).cloned().unwrap_or(RValue::Null);
271 Ok(make_condition_with_call(
272 &msg,
273 call,
274 &["simpleWarning", "warning", "condition"],
275 ))
276}
277
278#[builtin(name = "simpleMessage", min_args = 1)]
284fn builtin_simple_message(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
285 let msg = args
286 .first()
287 .and_then(|v| v.as_vector().and_then(|rv| rv.as_character_scalar()))
288 .unwrap_or_default();
289 let call = args.get(1).cloned().unwrap_or(RValue::Null);
290 Ok(make_condition_with_call(
291 &msg,
292 call,
293 &["simpleMessage", "message", "condition"],
294 ))
295}
296
297#[builtin(name = "conditionMessage", min_args = 1)]
302fn builtin_condition_message(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
303 match args.first() {
304 Some(RValue::List(list)) => {
305 for (name, val) in &list.values {
306 if name.as_deref() == Some("message") {
307 return Ok(val.clone());
308 }
309 }
310 Ok(RValue::Null)
311 }
312 _ => Err(RError::new(
313 RErrorKind::Argument,
314 "conditionMessage requires a condition object".to_string(),
315 )),
316 }
317}
318
319#[builtin(name = "conditionCall", min_args = 1)]
324fn builtin_condition_call(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
325 match args.first() {
326 Some(RValue::List(list)) => {
327 for (name, val) in &list.values {
328 if name.as_deref() == Some("call") {
329 return Ok(val.clone());
330 }
331 }
332 Ok(RValue::Null)
333 }
334 _ => Err(RError::new(
335 RErrorKind::Argument,
336 "conditionCall requires a condition object".to_string(),
337 )),
338 }
339}
340
341#[builtin(name = "invokeRestart", min_args = 1)]
346fn builtin_invoke_restart(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
347 let restart_name = args
348 .first()
349 .and_then(|v| v.as_vector().and_then(|rv| rv.as_character_scalar()))
350 .ok_or_else(|| {
351 RError::new(
352 RErrorKind::Argument,
353 "restart name must be a string".to_string(),
354 )
355 })?;
356 Err(RError::other(restart_name))
358}