diag pretty
This commit is contained in:
parent
adba163511
commit
e70ff6ef9d
|
@ -1,3 +1,3 @@
|
|||
#!/usr/bin/env kabel
|
||||
declare p = 5;
|
||||
declare p = 51241Q12;
|
||||
debug: print using (3 + 5) * 2;
|
||||
|
|
108
examples/try.kab
108
examples/try.kab
|
@ -1,3 +1,111 @@
|
|||
"string lit";
|
||||
("string lit");# comment
|
||||
12345*(60+80);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"aaaaaa
|
|
@ -41,7 +41,8 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
eprintln!("ICE! Please report this to the kabel developers, along with what you were doing: {:?}", e);
|
||||
}
|
||||
KError::UserError(diags) => {
|
||||
eprintln!("{}", libkabel::diagnostics::emitters::basic::BasicEmitter::emit(diags, text_source));
|
||||
//eprintln!("{}", libkabel::diagnostics::emitters::basic::BasicEmitter::emit(diags, text_source));
|
||||
eprintln!("{}", libkabel::diagnostics::emitters::pretty::PrettyEmitter::emit(diags, text_source, argv[1].clone()));
|
||||
}
|
||||
}
|
||||
std::process::exit(1);
|
||||
|
|
|
@ -7,7 +7,7 @@ pub struct BasicEmitter;
|
|||
impl Emitter for BasicEmitter {
|
||||
type Output = String;
|
||||
|
||||
fn emit(diag: Vec<Diagnostic>, _source: String) -> Self::Output {
|
||||
fn emit(diag: Vec<Diagnostic>, _source: String, _source_name: String) -> Self::Output {
|
||||
let mut output = String::new();
|
||||
|
||||
for msg in diag {
|
||||
|
|
|
@ -3,8 +3,9 @@ use crate::diagnostics::Diagnostic;
|
|||
pub mod basic;
|
||||
#[cfg(feature = "pretty-emitter")]
|
||||
pub mod pretty;
|
||||
pub(crate) mod util;
|
||||
|
||||
pub trait Emitter {
|
||||
type Output;
|
||||
fn emit(diag: Vec<Diagnostic>, source: String) -> Self::Output;
|
||||
fn emit(diag: Vec<Diagnostic>, source: String, source_name: String) -> Self::Output;
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
use crate::diagnostics::{Diagnostic, DiagnosticType};
|
||||
use crate::diagnostics::emitters::Emitter;
|
||||
use std::fmt::Write;
|
||||
use colored::{ColoredString, Colorize};
|
||||
use crate::diagnostics::emitters::util::{get_line, pos_to_line_col};
|
||||
|
||||
pub struct PrettyEmitter;
|
||||
|
||||
impl Emitter for PrettyEmitter {
|
||||
type Output = String;
|
||||
|
||||
fn emit(diag: Vec<Diagnostic>, source: String, source_name: String) -> Self::Output {
|
||||
let mut output = String::new();
|
||||
|
||||
for msg in diag {
|
||||
|
||||
match msg.diag_type {
|
||||
DiagnosticType::Error => {
|
||||
write!(output, "{}", "error".bold().red()).unwrap();
|
||||
}
|
||||
DiagnosticType::Warning => {
|
||||
write!(output, "{}", "warning".bold().yellow()).unwrap();
|
||||
}
|
||||
_ => { continue; }
|
||||
}
|
||||
|
||||
writeln!(output, ": {}", msg.message.bold()).unwrap();
|
||||
|
||||
let mut biggest_line_no = 0;
|
||||
|
||||
for span in &msg.spans {
|
||||
let (s_line, _) = pos_to_line_col(span.span.start, &source);
|
||||
let (e_line, _) = pos_to_line_col(span.span.start, &source);
|
||||
if s_line > biggest_line_no {
|
||||
biggest_line_no = s_line;
|
||||
}
|
||||
if e_line > biggest_line_no {
|
||||
biggest_line_no = e_line;
|
||||
}
|
||||
}
|
||||
|
||||
let line_no_padding = biggest_line_no.to_string().len();
|
||||
|
||||
for labeled_span in &msg.spans {
|
||||
let (line, start_col) = pos_to_line_col(labeled_span.span.start, &source);
|
||||
let (_, end_col) = pos_to_line_col(labeled_span.span.end, &source);
|
||||
|
||||
writeln!(output, "{}{} {}:{}:{}", " ".repeat(line_no_padding), "-->".bright_blue().bold(), source_name, line, start_col).unwrap();
|
||||
|
||||
let line_hdr_padding = line.to_string().len() + 1;
|
||||
|
||||
writeln!(output, "{}{}", " ".repeat(line_hdr_padding), "|".bright_blue().bold()).unwrap();
|
||||
writeln!(output, "{}{} {} {}", line.to_string().bright_blue().bold(), " ".repeat(line_no_padding - line.to_string().len()), "|".bright_blue().bold(), get_line(line, &source).unwrap()).unwrap();
|
||||
|
||||
|
||||
|
||||
let mut end_char = match labeled_span.span_type {
|
||||
DiagnosticType::Error => '^'.to_string().bold().red(),
|
||||
DiagnosticType::Warning => '^'.to_string().bold().yellow(),
|
||||
DiagnosticType::Help => {
|
||||
if labeled_span.label.is_some() {
|
||||
'-'.to_string().bold().bright_blue()
|
||||
} else {
|
||||
'~'.to_string().bold().bright_blue()
|
||||
}
|
||||
},
|
||||
DiagnosticType::SecondaryError => {
|
||||
if labeled_span.label.is_some() {
|
||||
'-'.to_string().bold().red()
|
||||
} else {
|
||||
'~'.to_string().bold().red()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let message = match (labeled_span.span_type, labeled_span.label.clone()) {
|
||||
(_, None) => ColoredString::from(""),
|
||||
(DiagnosticType::Error, Some(e)) => e.bold().red(),
|
||||
(DiagnosticType::Warning, Some(e)) => e.bold().yellow(),
|
||||
(DiagnosticType::Help, Some(e)) => e.bold().bright_blue(),
|
||||
(DiagnosticType::SecondaryError, Some(e)) => e.bold().red()
|
||||
};
|
||||
|
||||
println!("{} {}", labeled_span.span.start, labeled_span.span.end);
|
||||
println!("{} {}", end_col, start_col);
|
||||
|
||||
let underline_length = if end_col == start_col {
|
||||
end_char = "".into();
|
||||
1
|
||||
} else if end_col < start_col {
|
||||
0
|
||||
} else {
|
||||
end_col - start_col
|
||||
};
|
||||
|
||||
let underline = match labeled_span.span_type {
|
||||
DiagnosticType::Error => "^".repeat(underline_length).to_string().bold().red(),
|
||||
DiagnosticType::Warning => "^".repeat(underline_length).to_string().bold().yellow(),
|
||||
DiagnosticType::Help => "~".repeat(underline_length).to_string().bold().bright_blue(),
|
||||
DiagnosticType::SecondaryError => "~".repeat(underline_length).to_string().bold().red()
|
||||
};
|
||||
|
||||
writeln!(output, "{}{} {}{}{} {}", " ".repeat(line_hdr_padding), "|".bright_blue().bold(), " ".repeat(start_col-1), underline, end_char, message).unwrap();
|
||||
writeln!(output).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
pub fn pos_to_line_col(pos: usize, source: &str) -> (usize, usize) {
|
||||
let mut line = 0;
|
||||
let mut col = 1;
|
||||
for (n, c) in source.chars().enumerate() {
|
||||
if c == '\n' {
|
||||
line += 1;
|
||||
col = 0;
|
||||
} else {
|
||||
col += 1;
|
||||
}
|
||||
if n == pos {
|
||||
break;
|
||||
}
|
||||
}
|
||||
(line, col)
|
||||
}
|
||||
|
||||
pub fn get_line(line: usize, source: &str) -> Option<&str> {
|
||||
source.lines().nth(line)
|
||||
}
|
|
@ -6,14 +6,16 @@ pub mod emitters;
|
|||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Diagnostic {
|
||||
pub diag_type: DiagnosticType,
|
||||
pub spans: Vec<SpanWithLabel>
|
||||
pub spans: Vec<SpanWithLabel>,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum DiagnosticType {
|
||||
Error,
|
||||
Warning,
|
||||
Help
|
||||
Help,
|
||||
SecondaryError
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
pub struct Span {
|
||||
pub start: u64,
|
||||
pub end: u64
|
||||
pub start: usize,
|
||||
pub end: usize
|
||||
}
|
||||
|
||||
impl Span {
|
||||
pub fn new(start: u64, end: u64) -> Self {
|
||||
pub fn new(start: usize, end: usize) -> Self {
|
||||
Self { start, end }
|
||||
}
|
||||
}
|
|
@ -23,8 +23,8 @@ pub fn lexer(text_source: &str) -> Result<Vec<Token>, KError> {
|
|||
|
||||
let mut chars = text_source.chars().peekable();
|
||||
|
||||
let mut pos: u64 = 0;
|
||||
let mut span_start: u64 = 0;
|
||||
let mut pos: usize = 0;
|
||||
let mut span_start: usize = 0;
|
||||
|
||||
while let Some(c) = chars.next() {
|
||||
pos += 1;
|
||||
|
@ -53,21 +53,39 @@ pub fn lexer(text_source: &str) -> Result<Vec<Token>, KError> {
|
|||
current_token.push(c);
|
||||
}
|
||||
} else {
|
||||
continue; // we're at the end. we should bring a user error
|
||||
// because this string was not properly delimited
|
||||
|
||||
return Err(KError::UserError(vec![
|
||||
Diagnostic {
|
||||
diag_type: DiagnosticType::Error,
|
||||
message: "unterminated string literal".to_string(),
|
||||
spans: vec![
|
||||
SpanWithLabel {
|
||||
span: Span::new(span_start-1, span_start-1),
|
||||
span_type: DiagnosticType::SecondaryError,
|
||||
label: Some("string began here".to_string())
|
||||
},
|
||||
SpanWithLabel {
|
||||
span: Span::new(pos, pos),
|
||||
span_type: DiagnosticType::Error,
|
||||
label: Some("expected end quote here".to_string())
|
||||
}
|
||||
]
|
||||
}
|
||||
]));
|
||||
}
|
||||
}
|
||||
State::Numbering => {
|
||||
current_token.push(c);
|
||||
// If next char isn't numeric, is at end of this number literal
|
||||
if let Some(c_peek) = chars.peek() {
|
||||
current_token.push(c);
|
||||
if !c_peek.is_ascii_digit() {
|
||||
|
||||
let num = match current_token.parse::<f64>() {
|
||||
Ok(n) => n,
|
||||
Err(_) => {
|
||||
println!("{} {}", span_start, pos);
|
||||
return Err(KError::UserError(vec![
|
||||
Diagnostic {
|
||||
message: "invalid numeric literal".to_string(),
|
||||
diag_type: DiagnosticType::Error,
|
||||
spans: vec![
|
||||
SpanWithLabel {
|
||||
|
@ -87,7 +105,6 @@ pub fn lexer(text_source: &str) -> Result<Vec<Token>, KError> {
|
|||
));
|
||||
state = State::BuildingToken;
|
||||
current_token = String::new();
|
||||
span_start = pos + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue