use std::io::{self, IsTerminal};
use std::path::Path;
use crate::ast::Declaration;
use crate::lexer::{Lexer, LexerError};
use crate::parser::{ParseError, Parser};
use crate::type_checker::TypeChecker;
struct Colors {
green_bold: &'static str,
red_bold: &'static str,
bold: &'static str,
dim: &'static str,
reset: &'static str,
}
impl Colors {
fn new(enabled: bool) -> Self {
if enabled {
Colors {
green_bold: "\x1b[1;32m",
red_bold: "\x1b[1;31m",
bold: "\x1b[1m",
dim: "\x1b[2m",
reset: "\x1b[0m",
}
} else {
Colors {
green_bold: "",
red_bold: "",
bold: "",
dim: "",
reset: "",
}
}
}
}
fn count_declarations(decls: &[Declaration]) -> usize {
let mut count = 0;
for decl in decls {
count += 1;
if let Declaration::Epistemic(eb) = decl {
count += count_declarations(&eb.body);
}
}
count
}
pub fn run_check(file: &str, no_color: bool) -> i32 {
let use_color = !no_color && io::stdout().is_terminal();
let c = Colors::new(use_color);
let path = Path::new(file);
let filename = path.file_name()
.map(|n| n.to_string_lossy().into_owned())
.unwrap_or_else(|| file.to_string());
let source = match std::fs::read_to_string(path) {
Ok(s) => s,
Err(_) => {
eprintln!(
"{}X File not found: {}{}",
c.red_bold, file, c.reset
);
return 2;
}
};
let tokens = match Lexer::new(&source, file).tokenize() {
Ok(t) => t,
Err(LexerError { message, line, column }) => {
let loc = if column > 0 {
format!(":{line}:{column}")
} else {
format!(":{line}")
};
eprintln!(
"{}X {filename}{loc}{} {message}",
c.red_bold, c.reset
);
return 1;
}
};
let token_count = tokens.len();
let mut parser = Parser::new(tokens);
let program = match parser.parse() {
Ok(p) => p,
Err(ParseError { message, line, column }) => {
let loc = if column > 0 {
format!(":{line}:{column}")
} else {
format!(":{line}")
};
eprintln!(
"{}X {filename}{loc}{} Parse error: {message}",
c.red_bold, c.reset
);
return 1;
}
};
let declaration_count = count_declarations(&program.declarations);
let type_errors = TypeChecker::new(&program).check();
if !type_errors.is_empty() {
eprintln!(
"{}X {filename}{} {} type error(s)",
c.red_bold, c.reset, type_errors.len()
);
for te in &type_errors {
eprintln!(" error [line {}]: {}", te.line, te.message);
}
return 1;
}
println!(
"{}\u{2713}{} {}{filename}{} {}{token_count} tokens \u{00B7} {declaration_count} declarations \u{00B7} 0 errors{}",
c.green_bold, c.reset,
c.bold, c.reset,
c.dim, c.reset,
);
0
}