Skip to content

Commit 3e48bad

Browse files
committed
Add support for tty vs non-tty error printing
The compiler will first detect if it is writing to a tty, only then it will print ascii codes for colored output. If it is writing to a non-tty output, it will default to text mode.
1 parent 6a7c5d0 commit 3e48bad

File tree

4 files changed

+65
-38
lines changed

4 files changed

+65
-38
lines changed

src/error.rs

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//! caller. It has various From<> traits deriving from both kinds and
1212
//! location errors, often dropping the location and only carrying kind.
1313
use crate::lexer::Location;
14+
use crate::qcceprintln;
1415
use std::error::Error;
1516
use std::fmt::{Debug, Display};
1617

@@ -141,7 +142,7 @@ impl QccError {
141142
#[inline]
142143
/// Report a message alongwith error.
143144
pub(crate) fn report(&self, msg: &str) {
144-
eprintln!("{} {}", self, msg);
145+
qcceprintln!("{} {}", self, msg);
145146
}
146147

147148
#[inline]
@@ -153,11 +154,7 @@ impl QccError {
153154

154155
impl Display for QccError {
155156
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156-
write!(
157-
f,
158-
"\x1b[99;1mqcc\x1b[0m: \x1b[91;1merror:\x1b[0m {}",
159-
self.0
160-
)
157+
write!(f, "{}", self.0)
161158
}
162159
}
163160

@@ -247,14 +244,15 @@ impl QccErrorLoc {
247244
self.1.replace(new_loc);
248245
}
249246

250-
/// Reporter to print source with annotation.
247+
/// Reporter to print source with annotation. This takes a source line and
248+
/// calculates the exact location of the origin of error and marks it.
251249
pub(crate) fn report(&self, src: String) {
252250
let row = self.1.borrow().row().to_string();
253251
let mut col = self.1.borrow().col();
254252

255253
let src_fmt = format!(" {} | {}", row, src);
256254

257-
eprintln!("{}", self);
255+
qcceprintln!("{}", self);
258256
eprint!("{src_fmt}");
259257

260258
// +11 for chars for manual inserted whitespces in src_fmt, +length of
@@ -350,6 +348,37 @@ impl From<&str> for QccErrorLoc {
350348
}
351349
}
352350

351+
/// This is a wrapper to eprintln! but prepends a header of "qcc: error:" before
352+
/// printing the error. It also ensures that binary color codes aren't printed
353+
/// in non-terminal stderr.
354+
///
355+
/// # Examples
356+
///
357+
/// ```
358+
/// use qcc::error::QccErrorKind;
359+
/// use qcc::qcceprintln;
360+
///
361+
/// // You must always pass a formatter first, followed by a comma-separated
362+
/// // expressions to fill holes in the formatter.
363+
/// let err = QccErrorKind::TypeError;
364+
/// // qcceprintln!(err); // Missing formatter will lead to error
365+
/// qcceprintln!("{}", err);
366+
/// ```
367+
#[macro_export]
368+
macro_rules! qcceprintln {
369+
() => {
370+
eprintln!();
371+
};
372+
($fmt:literal $(, $args:expr)* $(,)?) => {{
373+
use std::io::IsTerminal;
374+
if std::io::stderr().is_terminal() {
375+
eprintln!(concat!("\x1b[99;1mqcc\x1b[0m: \x1b[91;1merror:\x1b[0m ", $fmt), $($args),*);
376+
} else {
377+
eprintln!(concat!("qcc: error: ", $fmt), $($args),*);
378+
}
379+
}};
380+
}
381+
353382
#[cfg(test)]
354383
mod tests {
355384
use super::*;
@@ -361,19 +390,13 @@ mod tests {
361390
let e1: Result<()> = Err(UnexpectedAttr.into());
362391
match e1 {
363392
Ok(_) => unreachable!(),
364-
Err(ref e) => assert_eq!(
365-
e.to_string(),
366-
"\x1b[99;1mqcc\x1b[0m: \x1b[91;1merror:\x1b[0m unexpected attribute"
367-
),
393+
Err(ref e) => assert_eq!(e.to_string(), "unexpected attribute"),
368394
}
369395

370396
let e2: Result<()> = Err(NoFile.into());
371397
match e2 {
372398
Ok(_) => unreachable!(),
373-
Err(ref e) => assert_eq!(
374-
e.to_string(),
375-
"\x1b[99;1mqcc\x1b[0m: \x1b[91;1merror:\x1b[0m no such file"
376-
),
399+
Err(ref e) => assert_eq!(e.to_string(), "no such file"),
377400
}
378401
Ok(())
379402
}

src/inference.rs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
use crate::ast::{Expr, FunctionAST, LiteralAST, Qast, QccCell, VarAST};
33
use crate::error::{QccError, QccErrorKind, Result};
44
use crate::mangle::{mangle, mangle_module};
5+
use crate::qcceprintln;
56
use crate::types::Type;
67
use std::borrow::{Borrow, BorrowMut};
78

@@ -266,20 +267,23 @@ pub fn infer(ast: &mut Qast) -> Result<()> {
266267
match untyped {
267268
Ok(expr) => {
268269
// unknown type of expression err
269-
let err: QccError = QccErrorKind::UnknownType.into();
270270
let expr = expr.as_ref().borrow();
271-
err.report(
272-
format!("for `{}` {}", expr, expr.get_location()).as_str(),
271+
qcceprintln!(
272+
"{} for `{}` {}",
273+
QccErrorKind::UnknownType,
274+
expr,
275+
expr.get_location()
273276
);
274277
}
275278
Err(err) => {
276279
// err is returned
277-
let row = instruction.as_ref().borrow().get_location().row();
278-
err.report(&format!(
279-
"on\n\t{}\t{}",
280-
row,
281-
instruction.as_ref().borrow()
282-
));
280+
let instruction = instruction.as_ref().borrow();
281+
qcceprintln!(
282+
"{} on\n\t{}\t{}",
283+
err,
284+
instruction.get_location().row(),
285+
instruction
286+
);
283287
}
284288
}
285289
}
@@ -310,23 +314,25 @@ pub fn infer(ast: &mut Qast) -> Result<()> {
310314
let err: QccError = QccErrorKind::TypeMismatch.into();
311315
let last_expr = last.as_ref().borrow();
312316
if last_instruction_type.is_none() {
313-
err.report(&format!(
314-
"between\n\t`{}` ({}) and `{}` ({}) {}",
317+
qcceprintln!(
318+
"{} between\n\t`{}` ({}) and `{}` ({}) {}",
319+
err,
315320
last_expr,
316321
Type::Bottom,
317322
fn_name,
318323
fn_return_type,
319324
last.as_ref().borrow().get_location()
320-
));
325+
);
321326
} else {
322-
err.report(&format!(
323-
"between\n\t`{}` ({}) and `{}` ({}) {}",
327+
qcceprintln!(
328+
"{} between\n\t`{}` ({}) and `{}` ({}) {}",
329+
err,
324330
last_expr,
325331
last_instruction_type.unwrap(),
326332
fn_name,
327333
fn_return_type,
328334
last.as_ref().borrow().get_location()
329-
));
335+
);
330336
}
331337
}
332338
}

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ fn main() -> Result<()> {
6060
let args = args.iter().map(|s| s.as_str()).collect();
6161

6262
if let Err(err) = init_session(args) {
63-
eprintln!("{err}");
63+
qcceprintln!("{}", err);
6464
}
6565

6666
Ok(())

src/parser.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::config::*;
66
use crate::error::{QccError, QccErrorKind, QccErrorLoc, Result};
77
use crate::lexer::{Lexer, Location};
88
use crate::mangle::{mangle_module, sanitize};
9+
use crate::qcceprintln;
910
use crate::types::Type;
1011
use crate::utils::usage;
1112
use std::collections::BTreeSet;
@@ -81,8 +82,7 @@ impl Parser {
8182
"--print-qasm" => config.print_qasm = true,
8283
"--debug" => config.debug = true,
8384
_ => {
84-
let err: QccError = QccErrorKind::NoSuchArg.into();
85-
err.report(option);
85+
qcceprintln!("{}", QccErrorKind::NoSuchArg);
8686
return Err(QccErrorKind::CmdlineErr)?;
8787
}
8888
}
@@ -100,8 +100,7 @@ impl Parser {
100100
return Ok(None);
101101
}
102102
_ => {
103-
let err: QccError = QccErrorKind::NoSuchArg.into();
104-
err.report(option);
103+
qcceprintln!("{}", QccErrorKind::NoSuchArg);
105104
return Err(QccErrorKind::CmdlineErr)?;
106105
}
107106
}
@@ -125,8 +124,7 @@ impl Parser {
125124

126125
if !Path::new(&path).is_file() {
127126
let err: QccError = QccErrorKind::NoFile.into();
128-
let s = format!("{}", path);
129-
err.report(&s);
127+
qcceprintln!("{} {}", err, path);
130128
return Err(QccErrorKind::CmdlineErr)?;
131129
}
132130

0 commit comments

Comments
 (0)