Skip to content

Commit 55d3356

Browse files
committed
Add Quiet function for message suppression during evaluation
Implement Quiet[expr], Quiet[expr, All/None], Quiet[expr, {msgs}], and Quiet[expr, moff, mon] with proper Check interaction. Migrate ~50 eprintln message sites to emit_message() so they respect Quiet suppression via a thread-local quiet level and warning buffer snapshot/restore mechanism.
1 parent 74c177c commit 55d3356

23 files changed

+554
-129
lines changed

functions.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4443,7 +4443,7 @@ QuestionSelector,,,,13.1,
44434443
QueueingNetworkProcess,,,,9,2889
44444444
QueueingProcess,,,,9,2129
44454445
QueueProperties,,,,9,2435
4446-
Quiet,,,,6,284
4446+
Quiet,Evaluate expression suppressing messages,,contextual,6,284
44474447
QuietEcho,,,,12.2,
44484448
Quit,Terminates the kernel session with optional exit code,,effectful,1,788
44494449
Quotient,Returns the integer quotient of division,,pure,1,649

src/evaluator/assignment.rs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,10 @@ pub fn set_attributes_from_value(
177177
.is_some_and(|attrs| attrs.contains(&"Locked".to_string()))
178178
});
179179
if is_locked {
180-
eprintln!("Attributes::locked: Symbol {} is locked.", sym_name);
180+
crate::emit_message(&format!(
181+
"Attributes::locked: Symbol {} is locked.",
182+
sym_name
183+
));
181184
return Ok(rhs_value.clone());
182185
}
183186

@@ -196,7 +199,10 @@ pub fn set_attributes_from_value(
196199
} else {
197200
// Non-symbol attribute — emit warning
198201
let attr_str = expr_to_string(attr_expr);
199-
eprintln!("Attributes::attnf: {} is not a known attribute.", attr_str);
202+
crate::emit_message(&format!(
203+
"Attributes::attnf: {} is not a known attribute.",
204+
attr_str
205+
));
200206
has_error = true;
201207
}
202208
}
@@ -256,7 +262,10 @@ pub fn set_ast(lhs: &Expr, rhs: &Expr) -> Result<Expr, InterpreterError> {
256262
// Check Protected attribute
257263
if is_symbol_protected(&var_name) {
258264
let rhs_value = evaluate_expr_to_expr(rhs)?;
259-
eprintln!("Part::wrsym: Symbol {} is Protected.", var_name);
265+
crate::emit_message(&format!(
266+
"Part::wrsym: Symbol {} is Protected.",
267+
var_name
268+
));
260269
return Ok(rhs_value);
261270
}
262271

@@ -338,7 +347,10 @@ pub fn set_ast(lhs: &Expr, rhs: &Expr) -> Result<Expr, InterpreterError> {
338347

339348
// Check Protected attribute
340349
if is_symbol_protected(var_name) {
341-
eprintln!("Set::wrsym: Symbol {} is Protected.", var_name);
350+
crate::emit_message(&format!(
351+
"Set::wrsym: Symbol {} is Protected.",
352+
var_name
353+
));
342354
return Ok(rhs_value);
343355
}
344356

@@ -426,7 +438,10 @@ pub fn set_ast(lhs: &Expr, rhs: &Expr) -> Result<Expr, InterpreterError> {
426438
.is_some_and(|attrs| attrs.contains(&"Protected".to_string()))
427439
});
428440
if is_user_protected {
429-
eprintln!("Set::wrsym: Symbol {} is Protected.", func_name);
441+
crate::emit_message(&format!(
442+
"Set::wrsym: Symbol {} is Protected.",
443+
func_name
444+
));
430445
return Ok(rhs_value);
431446
}
432447

@@ -507,7 +522,10 @@ pub fn set_delayed_ast(
507522
.is_some_and(|attrs| attrs.contains(&"Protected".to_string()))
508523
});
509524
if is_user_protected {
510-
eprintln!("SetDelayed::wrsym: Symbol {} is Protected.", func_name);
525+
crate::emit_message(&format!(
526+
"SetDelayed::wrsym: Symbol {} is Protected.",
527+
func_name
528+
));
511529
return Ok(Expr::Identifier("Null".to_string()));
512530
}
513531

@@ -668,7 +686,10 @@ pub fn set_delayed_ast(
668686
// Handle simple identifier assignment: a := expr (OwnValues)
669687
if let Expr::Identifier(var_name) = lhs {
670688
if is_symbol_protected(var_name) {
671-
eprintln!("SetDelayed::wrsym: Symbol {} is Protected.", var_name);
689+
crate::emit_message(&format!(
690+
"SetDelayed::wrsym: Symbol {} is Protected.",
691+
var_name
692+
));
672693
return Ok(Expr::Identifier("Null".to_string()));
673694
}
674695
// Store the unevaluated body — it will be re-evaluated each time the symbol is accessed

src/evaluator/attributes.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ pub fn get_builtin_attributes(name: &str) -> Vec<&'static str> {
166166
| "CompoundExpression" | "Switch" | "Which" | "Catch" | "Throw"
167167
| "Clear" | "ClearAll" | "Condition" | "Off" | "On" | "TimeConstrained"
168168
| "MemoryConstrained" | "TagUnset" | "NProduct" | "Definition"
169-
| "FullDefinition" | "Attributes" => {
169+
| "FullDefinition" | "Attributes" | "Quiet" => {
170170
vec!["HoldAll", "Protected"]
171171
}
172172
"Remove" => vec!["HoldAll", "Locked", "Protected"],
@@ -468,7 +468,6 @@ pub fn get_builtin_attributes(name: &str) -> Vec<&'static str> {
468468
| "Pause"
469469
| "Check"
470470
| "CheckAbort"
471-
| "Quiet"
472471
| "FilterRules"
473472
| "Operate"
474473
| "ReverseSort"

src/evaluator/core_eval.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,6 +1217,20 @@ pub fn evaluate_expr_to_expr_inner(
12171217
}
12181218
}
12191219
}
1220+
// Special handling for Quiet[expr], Quiet[expr, msgs], Quiet[expr, moff, mon]
1221+
if name == "Quiet" {
1222+
if args.is_empty() || args.len() > 3 {
1223+
crate::emit_message(&format!(
1224+
"Quiet::argb: Quiet called with {} arguments; between 1 and 3 arguments are expected.",
1225+
args.len()
1226+
));
1227+
return Ok(Expr::FunctionCall {
1228+
name: "Quiet".to_string(),
1229+
args: args.to_vec(),
1230+
});
1231+
}
1232+
return crate::functions::control_flow_ast::quiet_ast(args);
1233+
}
12201234
// Special handling for Switch - lazy evaluation of branches
12211235
if name == "Switch" && args.len() >= 3 {
12221236
return crate::functions::control_flow_ast::switch_ast(args);
@@ -1751,11 +1765,10 @@ pub fn evaluate_expr_to_expr_inner(
17511765
index: index.clone(),
17521766
};
17531767
let part_str = crate::syntax::expr_to_string(&original);
1754-
eprintln!();
1755-
eprintln!(
1768+
crate::emit_message(&format!(
17561769
"Part::partd: Part specification {} is longer than depth of object.",
17571770
part_str
1758-
);
1771+
));
17591772
}
17601773
}
17611774
Ok(result)

src/evaluator/dispatch/attributes.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ pub fn dispatch_attributes(
4343
if let Some(existing) = attrs.get(func_name)
4444
&& existing.contains(&"Locked".to_string())
4545
{
46-
eprintln!("Attributes::locked: Symbol {} is locked.", func_name);
46+
crate::emit_message(&format!(
47+
"Attributes::locked: Symbol {} is locked.",
48+
func_name
49+
));
4750
locked = true;
4851
continue;
4952
}
@@ -97,7 +100,10 @@ pub fn dispatch_attributes(
97100
if let Some(existing) = attrs.get(func_name)
98101
&& existing.contains(&"Locked".to_string())
99102
{
100-
eprintln!("Attributes::locked: Symbol {} is locked.", func_name);
103+
crate::emit_message(&format!(
104+
"Attributes::locked: Symbol {} is locked.",
105+
func_name
106+
));
101107
continue;
102108
}
103109
if let Some(entry) = attrs.get_mut(func_name) {
@@ -141,7 +147,10 @@ pub fn dispatch_attributes(
141147
}
142148
};
143149
if is_locked {
144-
eprintln!("Protect::locked: Symbol {} is locked.", sym);
150+
crate::emit_message(&format!(
151+
"Protect::locked: Symbol {} is locked.",
152+
sym
153+
));
145154
continue;
146155
}
147156
let was_protected = crate::FUNC_ATTRS.with(|m| {

src/evaluator/dispatch/boolean_functions.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,10 @@ pub fn dispatch_boolean_functions(
1616
if args.len() == 1 {
1717
return Some(crate::functions::boolean_ast::not_ast(args));
1818
} else {
19-
println!(
20-
"\nNot::argx: Not called with {} arguments; 1 argument is expected.",
19+
crate::emit_message(&format!(
20+
"Not::argx: Not called with {} arguments; 1 argument is expected.",
2121
args.len()
22-
);
23-
use std::io::{self, Write};
24-
io::stdout().flush().ok();
22+
));
2523
}
2624
}
2725
"Xor" if !args.is_empty() => {

src/evaluator/dispatch/evaluation_control.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,12 +157,10 @@ pub fn dispatch_evaluation_control(
157157
return Some(evaluate_expr_to_expr(&args[3]));
158158
}
159159
} else if args.len() < 2 || args.len() > 4 {
160-
println!(
161-
"\nIf::argb: If called with {} arguments; between 2 and 4 arguments are expected.",
160+
crate::emit_message(&format!(
161+
"If::argb: If called with {} arguments; between 2 and 4 arguments are expected.",
162162
args.len()
163-
);
164-
use std::io::{self, Write};
165-
io::stdout().flush().ok();
163+
));
166164
}
167165
}
168166
_ => {}

src/evaluator/dispatch/io_functions.rs

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ pub fn dispatch_io_functions(
2626
let content = match std::fs::read_to_string(&filename) {
2727
Ok(c) => c,
2828
Err(_) => {
29-
eprintln!("Get::noopen: Cannot open {}.", filename);
29+
crate::emit_message(&format!(
30+
"Get::noopen: Cannot open {}.",
31+
filename
32+
));
3033
return Some(Ok(Expr::Identifier("$Failed".to_string())));
3134
}
3235
};
@@ -66,7 +69,10 @@ pub fn dispatch_io_functions(
6669
match std::fs::write(&filename, to_write) {
6770
Ok(_) => return Some(Ok(Expr::Identifier("Null".to_string()))),
6871
Err(_e) => {
69-
eprintln!("Put::noopen: Cannot open {}.", filename);
72+
crate::emit_message(&format!(
73+
"Put::noopen: Cannot open {}.",
74+
filename
75+
));
7076
return Some(Ok(Expr::Identifier("$Failed".to_string())));
7177
}
7278
}
@@ -99,12 +105,18 @@ pub fn dispatch_io_functions(
99105
{
100106
Ok(mut file) => {
101107
if file.write_all(to_write.as_bytes()).is_err() {
102-
eprintln!("PutAppend::noopen: Cannot open {}.", filename);
108+
crate::emit_message(&format!(
109+
"PutAppend::noopen: Cannot open {}.",
110+
filename
111+
));
103112
return Some(Ok(Expr::Identifier("$Failed".to_string())));
104113
}
105114
}
106115
Err(_) => {
107-
eprintln!("PutAppend::noopen: Cannot open {}.", filename);
116+
crate::emit_message(&format!(
117+
"PutAppend::noopen: Cannot open {}.",
118+
filename
119+
));
108120
return Some(Ok(Expr::Identifier("$Failed".to_string())));
109121
}
110122
}
@@ -358,7 +370,10 @@ pub fn dispatch_io_functions(
358370
}
359371
};
360372
if !std::path::Path::new(&filename).exists() {
361-
eprintln!("OpenRead::noopen: Cannot open {}.", filename);
373+
crate::emit_message(&format!(
374+
"OpenRead::noopen: Cannot open {}.",
375+
filename
376+
));
362377
return Some(Ok(Expr::Identifier("$Failed".to_string())));
363378
}
364379
let id = crate::register_stream(
@@ -492,7 +507,7 @@ pub fn dispatch_io_functions(
492507
Some(name) => return Some(Ok(Expr::Identifier(name))),
493508
None => {
494509
let stream_str = crate::syntax::expr_to_string(&args[0]);
495-
eprintln!("{} is not open.", stream_str);
510+
crate::emit_message(&format!("{} is not open.", stream_str));
496511
return Some(Ok(Expr::FunctionCall {
497512
name: "Close".to_string(),
498513
args: args.to_vec(),
@@ -501,7 +516,7 @@ pub fn dispatch_io_functions(
501516
}
502517
}
503518
Expr::String(s) => {
504-
eprintln!("{} is not open.", s);
519+
crate::emit_message(&format!("{} is not open.", s));
505520
return Some(Ok(Expr::FunctionCall {
506521
name: "Close".to_string(),
507522
args: args.to_vec(),
@@ -880,7 +895,10 @@ pub fn dispatch_io_functions(
880895
match std::fs::write(&filename, &content) {
881896
Ok(_) => {}
882897
Err(_e) => {
883-
eprintln!("Save::noopen: Cannot open {}.", filename);
898+
crate::emit_message(&format!(
899+
"Save::noopen: Cannot open {}.",
900+
filename
901+
));
884902
return Some(Ok(Expr::Identifier("$Failed".to_string())));
885903
}
886904
}

src/evaluator/dispatch/math_functions.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,10 @@ pub fn dispatch_math_functions(
2323
if args.len() == 2 {
2424
return Some(crate::functions::math_ast::divide_ast(args));
2525
} else {
26-
println!(
27-
"\nDivide::argrx: Divide called with {} arguments; 2 arguments are expected.",
26+
crate::emit_message(&format!(
27+
"Divide::argrx: Divide called with {} arguments; 2 arguments are expected.",
2828
args.len()
29-
);
30-
use std::io::{self, Write};
31-
io::stdout().flush().ok();
29+
));
3230
return Some(Ok(Expr::FunctionCall {
3331
name: "Divide".to_string(),
3432
args: args.to_vec(),

src/evaluator/part_extraction.rs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,10 @@ fn part_take_unevaluated(expr: &Expr, index: &Expr) -> Expr {
3434

3535
fn part_take_warn(expr: &Expr, start: i64, end: i64) {
3636
let expr_str = crate::syntax::expr_to_string(expr);
37-
eprintln!();
38-
eprintln!(
37+
crate::emit_message(&format!(
3938
"Part::take: Cannot take positions {} through {} in {}.",
4039
start, end, expr_str
41-
);
40+
));
4241
}
4342

4443
fn extract_span_from_items(
@@ -276,8 +275,10 @@ pub fn extract_part_ast(
276275
} else {
277276
// Print warning to stderr and return unevaluated Part expression
278277
let expr_str = crate::syntax::expr_to_string(expr);
279-
eprintln!();
280-
eprintln!("Part::partw: Part {} of {} does not exist.", idx, expr_str);
278+
crate::emit_message(&format!(
279+
"Part::partw: Part {} of {} does not exist.",
280+
idx, expr_str
281+
));
281282
Ok(part_take_unevaluated(expr, index))
282283
}
283284
}
@@ -292,8 +293,10 @@ pub fn extract_part_ast(
292293
Ok(args[actual_idx as usize].clone())
293294
} else {
294295
let expr_str = crate::syntax::expr_to_string(expr);
295-
eprintln!();
296-
eprintln!("Part::partw: Part {} of {} does not exist.", idx, expr_str);
296+
crate::emit_message(&format!(
297+
"Part::partw: Part {} of {} does not exist.",
298+
idx, expr_str
299+
));
297300
Ok(part_take_unevaluated(expr, index))
298301
}
299302
}
@@ -321,8 +324,10 @@ pub fn extract_part_ast(
321324
Ok(parts[actual_idx as usize].clone())
322325
} else {
323326
let expr_str = crate::syntax::expr_to_string(expr);
324-
eprintln!();
325-
eprintln!("Part::partw: Part {} of {} does not exist.", idx, expr_str);
327+
crate::emit_message(&format!(
328+
"Part::partw: Part {} of {} does not exist.",
329+
idx, expr_str
330+
));
326331
Ok(part_take_unevaluated(expr, index))
327332
}
328333
}
@@ -334,8 +339,10 @@ pub fn extract_part_ast(
334339
Ok(Expr::String(chars[actual_idx as usize].to_string()))
335340
} else {
336341
// Print warning to stderr and return unevaluated Part expression
337-
eprintln!();
338-
eprintln!("Part::partw: Part {} of \"{}\" does not exist.", idx, s);
342+
crate::emit_message(&format!(
343+
"Part::partw: Part {} of \"{}\" does not exist.",
344+
idx, s
345+
));
339346
Ok(part_take_unevaluated(expr, index))
340347
}
341348
}

0 commit comments

Comments
 (0)