diag pretty
This commit is contained in:
parent
adba163511
commit
e70ff6ef9d
|
@ -1,3 +1,3 @@
|
||||||
#!/usr/bin/env kabel
|
#!/usr/bin/env kabel
|
||||||
declare p = 5;
|
declare p = 51241Q12;
|
||||||
debug: print using (3 + 5) * 2;
|
debug: print using (3 + 5) * 2;
|
||||||
|
|
108
examples/try.kab
108
examples/try.kab
|
@ -1,3 +1,111 @@
|
||||||
"string lit";
|
"string lit";
|
||||||
("string lit");# comment
|
("string lit");# comment
|
||||||
12345*(60+80);
|
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);
|
eprintln!("ICE! Please report this to the kabel developers, along with what you were doing: {:?}", e);
|
||||||
}
|
}
|
||||||
KError::UserError(diags) => {
|
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);
|
std::process::exit(1);
|
||||||
|
|
|
@ -7,7 +7,7 @@ pub struct BasicEmitter;
|
||||||
impl Emitter for BasicEmitter {
|
impl Emitter for BasicEmitter {
|
||||||
type Output = String;
|
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();
|
let mut output = String::new();
|
||||||
|
|
||||||
for msg in diag {
|
for msg in diag {
|
||||||
|
|
|
@ -3,8 +3,9 @@ use crate::diagnostics::Diagnostic;
|
||||||
pub mod basic;
|
pub mod basic;
|
||||||
#[cfg(feature = "pretty-emitter")]
|
#[cfg(feature = "pretty-emitter")]
|
||||||
pub mod pretty;
|
pub mod pretty;
|
||||||
|
pub(crate) mod util;
|
||||||
|
|
||||||
pub trait Emitter {
|
pub trait Emitter {
|
||||||
type Output;
|
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)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Diagnostic {
|
pub struct Diagnostic {
|
||||||
pub diag_type: DiagnosticType,
|
pub diag_type: DiagnosticType,
|
||||||
pub spans: Vec<SpanWithLabel>
|
pub spans: Vec<SpanWithLabel>,
|
||||||
|
pub message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum DiagnosticType {
|
pub enum DiagnosticType {
|
||||||
Error,
|
Error,
|
||||||
Warning,
|
Warning,
|
||||||
Help
|
Help,
|
||||||
|
SecondaryError
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||||
pub struct Span {
|
pub struct Span {
|
||||||
pub start: u64,
|
pub start: usize,
|
||||||
pub end: u64
|
pub end: usize
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Span {
|
impl Span {
|
||||||
pub fn new(start: u64, end: u64) -> Self {
|
pub fn new(start: usize, end: usize) -> Self {
|
||||||
Self { start, end }
|
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 chars = text_source.chars().peekable();
|
||||||
|
|
||||||
let mut pos: u64 = 0;
|
let mut pos: usize = 0;
|
||||||
let mut span_start: u64 = 0;
|
let mut span_start: usize = 0;
|
||||||
|
|
||||||
while let Some(c) = chars.next() {
|
while let Some(c) = chars.next() {
|
||||||
pos += 1;
|
pos += 1;
|
||||||
|
@ -53,21 +53,39 @@ pub fn lexer(text_source: &str) -> Result<Vec<Token>, KError> {
|
||||||
current_token.push(c);
|
current_token.push(c);
|
||||||
}
|
}
|
||||||
} else {
|
} 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 => {
|
State::Numbering => {
|
||||||
|
current_token.push(c);
|
||||||
// If next char isn't numeric, is at end of this number literal
|
// If next char isn't numeric, is at end of this number literal
|
||||||
if let Some(c_peek) = chars.peek() {
|
if let Some(c_peek) = chars.peek() {
|
||||||
current_token.push(c);
|
|
||||||
if !c_peek.is_ascii_digit() {
|
if !c_peek.is_ascii_digit() {
|
||||||
|
|
||||||
let num = match current_token.parse::<f64>() {
|
let num = match current_token.parse::<f64>() {
|
||||||
Ok(n) => n,
|
Ok(n) => n,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
println!("{} {}", span_start, pos);
|
||||||
return Err(KError::UserError(vec![
|
return Err(KError::UserError(vec![
|
||||||
Diagnostic {
|
Diagnostic {
|
||||||
|
message: "invalid numeric literal".to_string(),
|
||||||
diag_type: DiagnosticType::Error,
|
diag_type: DiagnosticType::Error,
|
||||||
spans: vec![
|
spans: vec![
|
||||||
SpanWithLabel {
|
SpanWithLabel {
|
||||||
|
@ -87,7 +105,6 @@ pub fn lexer(text_source: &str) -> Result<Vec<Token>, KError> {
|
||||||
));
|
));
|
||||||
state = State::BuildingToken;
|
state = State::BuildingToken;
|
||||||
current_token = String::new();
|
current_token = String::new();
|
||||||
span_start = pos + 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue