Skip to content

Commit

Permalink
add error-format component
Browse files Browse the repository at this point in the history
  • Loading branch information
xffxff committed Sep 26, 2023
1 parent cac9ca1 commit a2f5965
Show file tree
Hide file tree
Showing 11 changed files with 286 additions and 174 deletions.
33 changes: 33 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ lox-parse = { path="components/lox-parse" }
lox-lex = { path="components/lox-lex" }
lox-compile = { path="components/lox-compile" }
lox-execute = { path="components/lox-execute" }
lox-error-format = { path="components/lox-error-format" }
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
clap = { version = "4.4.4", features = ["derive"] }
12 changes: 12 additions & 0 deletions components/lox-error-format/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "lox-error-format"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
ariadne = "0.3.0"
salsa = { path="../salsa" }
lox-ir = { path="../lox-ir" }
eyre = "0.6.8"
115 changes: 115 additions & 0 deletions components/lox-error-format/src/format.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use std::{collections::HashMap, io::Cursor};

use ariadne::{Config, Label, Report, ReportKind, Source};
use lox_ir::input_file::InputFile;

/// Options for controlling error formatting when they are printed.
#[derive(Clone, Copy)]
pub struct FormatOptions {
/// Whether or not errors should use rich formatting with colors. This is generally turned on,
/// except in tests, where the escape codes obscure the error messages.
with_color: bool,
}

impl FormatOptions {
pub fn no_color() -> Self {
Self { with_color: false }
}
}

const DEFAULT_FORMATTING: FormatOptions = FormatOptions { with_color: true };

pub fn print_diagnostic(
db: &dyn crate::Db,
diagnostic: &lox_ir::diagnostic::Diagnostic,
) -> eyre::Result<()> {
Ok(ariadne_diagnostic(db, diagnostic, DEFAULT_FORMATTING)?.print(SourceCache::new(db))?)
}

pub fn format_diagnostics(
db: &dyn crate::Db,
diagnostics: &[lox_ir::diagnostic::Diagnostic],
) -> eyre::Result<String> {
format_diagnostics_with_options(db, diagnostics, DEFAULT_FORMATTING)
}

pub fn format_diagnostics_with_options(
db: &dyn crate::Db,
diagnostics: &[lox_ir::diagnostic::Diagnostic],
options: FormatOptions,
) -> eyre::Result<String> {
let mut output = Vec::new();
let mut cursor = Cursor::new(&mut output);
let mut cache = SourceCache::new(db);
for diagnostic in diagnostics {
let ariadne = ariadne_diagnostic(db, diagnostic, options)?;
ariadne.write(&mut cache, &mut cursor)?;
}
Ok(String::from_utf8(output)?)
}

fn ariadne_diagnostic<'a>(
_db: &'a dyn crate::Db,
diagnostic: &'a lox_ir::diagnostic::Diagnostic,
options: FormatOptions,
) -> eyre::Result<ariadne::Report<'a, ASpan>> {
let mut builder = Report::<ASpan>::build(
ReportKind::Error,
diagnostic.span.input_file,
diagnostic.span.start.into(),
)
.with_message(&diagnostic.message)
.with_config(Config::default().with_color(options.with_color));

for label in &diagnostic.labels {
builder = builder.with_label(Label::new(ASpan(label.span())).with_message(&label.message));
}

Ok(builder.finish())
}

struct SourceCache<'me> {
db: &'me dyn crate::Db,
map: HashMap<InputFile, Source>,
}

impl<'me> SourceCache<'me> {
pub fn new(db: &'me dyn crate::Db) -> Self {
Self {
db,
map: Default::default(),
}
}
}

impl ariadne::Cache<InputFile> for SourceCache<'_> {
fn fetch(&mut self, id: &InputFile) -> Result<&Source, Box<dyn std::fmt::Debug + '_>> {
Ok(self.map.entry(*id).or_insert_with(|| {
let source_text = id.source_text(self.db);
Source::from(source_text)
}))
}

fn display<'a>(&self, id: &'a InputFile) -> Option<Box<dyn std::fmt::Display + 'a>> {
let s = id.name(self.db).as_str(self.db).to_string();
Some(Box::new(s))
}
}

struct ASpan(lox_ir::span::FileSpan);

impl ariadne::Span for ASpan {
type SourceId = InputFile;

fn source(&self) -> &Self::SourceId {
&self.0.input_file
}

fn start(&self) -> usize {
self.0.start.into()
}

fn end(&self) -> usize {
self.0.end.into()
}
}
16 changes: 16 additions & 0 deletions components/lox-error-format/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#![feature(trait_upcasting)]
#![feature(try_blocks)]
#![allow(incomplete_features)]

mod format;

#[salsa::jar(db = Db)]
pub struct Jar();

pub trait Db: salsa::DbWithJar<Jar> + lox_ir::Db {}
impl<T> Db for T where T: salsa::DbWithJar<Jar> + lox_ir::Db {}

pub use format::format_diagnostics;
pub use format::format_diagnostics_with_options;
pub use format::print_diagnostic;
pub use format::FormatOptions;
2 changes: 1 addition & 1 deletion components/lox-ir/src/input_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::word::Word;

#[salsa::input]
pub struct InputFile {
name: Word,
pub name: Word,

// the raw contents of the input file, as a string
#[return_ref]
Expand Down
76 changes: 28 additions & 48 deletions lox_tests/arithmetic/output
Original file line number Diff line number Diff line change
@@ -1,48 +1,28 @@
Diagnostic {
severity: Error,
span: @7..@8,
message: "expected `;`",
labels: [
Label {
span: @7..@8,
message: "here",
},
],
children: [],
}
Diagnostic {
severity: Error,
span: @18..@19,
message: "expected `;`",
labels: [
Label {
span: @18..@19,
message: "here",
},
],
children: [],
}
Diagnostic {
severity: Error,
span: @29..@30,
message: "expected `;`",
labels: [
Label {
span: @29..@30,
message: "here",
},
],
children: [],
}
Diagnostic {
severity: Error,
span: @38..@39,
message: "expected `;`",
labels: [
Label {
span: @38..@39,
message: "here",
},
],
children: [],
}
Error: expected `;`
╭─[/home/zhoufan/workspace/lox/lox_tests/arithmetic.lox:3:1]
3 │ 1 + 2 * 3
│ ┬
│ ╰── here
───╯
Error: expected `;`
╭─[/home/zhoufan/workspace/lox/lox_tests/arithmetic.lox:5:1]
5 │ 1 - 2 / 3
│ ┬
│ ╰── here
───╯
Error: expected `;`
╭─[/home/zhoufan/workspace/lox/lox_tests/arithmetic.lox:7:1]
7 │ 1 + 2 * -3
│ ┬
│ ╰── here
───╯
Error: expected `;`
╭─[/home/zhoufan/workspace/lox/lox_tests/arithmetic.lox:7:10]
7 │ 1 + 2 * -3
│ ┬
│ ╰── here
───╯
76 changes: 28 additions & 48 deletions lox_tests/boolean/output
Original file line number Diff line number Diff line change
@@ -1,48 +1,28 @@
Diagnostic {
severity: Error,
span: @5..@10,
message: "expected `;`",
labels: [
Label {
span: @5..@10,
message: "here",
},
],
children: [],
}
Diagnostic {
severity: Error,
span: @11..@12,
message: "expected `;`",
labels: [
Label {
span: @11..@12,
message: "here",
},
],
children: [],
}
Diagnostic {
severity: Error,
span: @17..@18,
message: "expected `;`",
labels: [
Label {
span: @17..@18,
message: "here",
},
],
children: [],
}
Diagnostic {
severity: Error,
span: @18..@23,
message: "expected `;`",
labels: [
Label {
span: @18..@23,
message: "here",
},
],
children: [],
}
Error: expected `;`
╭─[/home/zhoufan/workspace/lox/lox_tests/boolean.lox:2:1]
2 │ false
│ ──┬──
│ ╰──── here
───╯
Error: expected `;`
╭─[/home/zhoufan/workspace/lox/lox_tests/boolean.lox:3:1]
3 │ !true
│ ┬
│ ╰── here
───╯
Error: expected `;`
╭─[/home/zhoufan/workspace/lox/lox_tests/boolean.lox:4:1]
4 │ !false
│ ┬
│ ╰── here
───╯
Error: expected `;`
╭─[/home/zhoufan/workspace/lox/lox_tests/boolean.lox:4:2]
4 │ !false
│ ──┬──
│ ╰──── here
───╯
Loading

0 comments on commit a2f5965

Please sign in to comment.