TGQE is a Crate that makes it easy to integrate error handling from various compiler frontends, and comes with error conversions for multiple libraries and tunable configuration environment variables. When the number of errors exceeds a threshold, the remaining errors can be stored in SQLite to maintain a good reading experience. It uses Ariadne as the rendering implementation by default.
This project has no special relationship with the other projects mentioned in this document.
tgqeis only a comprehensive integration library, it cannot help you write a good Lexer and Parser.
First, add tgqe to your project with the following command:
cargo add tgqeThen enable the relevant features in your Cargo.toml. The currently available features are:
[features]
renderer = ["ariadne", "dotenvy", "chrono"]
trans-chumsky = ["chumsky"]
trans-logos = ["logos"]
trans-winnow = ["winnow"]
store-to-db = ["sqlx", "sqlx-sqlite", "chrono", "tokio", "dotenvy"]
chumsky-full = ["renderer", "trans-chumsky", "store-to-db"]
winnow-full = ["renderer", "trans-winnow" , "store-to-db"]
logos-full = ["renderer", "trans-logos" , "store-to-db"]
all = ["chumsky-full", "winnow-full", "logos-full"]Since most of
logos's errors are compile-time errors, currently onlylogos::Spanis converted.
Here is a simple example:
use tgqe::base_types::*;
use tgqe::enums::TgqeLevelFilter;
use tgqe::singletons::{ICC, SourceManager};
mod lexer; // Your Custom Lexer Implements
#[tokio::main(flavor = "current_thread")] // Only required if the `store-to-db` feature is enabled
async fn main() {
if let Err(e) =
TgqeReader::store_file(&path)
{
eprintln!("{}", e);
std::process::exit(1);
}
let lines = SourceManager
.lock()
.unwrap()
.iter_line()
.filter(|line| {
line.filepath == path
});
let mut lexer = lexer::Lexer::new();
for line in lines {
let (_, errors) =
lexer.lex_line(&line.code);
if !errors.is_empty() {
let mut ctxs: Vec<TgqeCtx> =
errors
.iter()
.map(|e| {
to_ctx(e, &path)
})
.collect();
ICC.lock()
.unwrap()
.report(&mut ctxs) // Report the errors
.await;
}
}
}You can also use it as an integration for your hand-written compiler frontend.
Sometimes what we need is not a specific error, but an object that "looks like an error and can express the information we need".
tgqe exposes the following main information structs:
pub struct TgqeCtx {
pub position: TgqePosition,
pub err_info: TgqeErrorInfo,
pub hints: String,
pub labels: String,
pub publisher: String,
pub ns_timestamp: i64,
}
pub struct TgqePosition {
pub coord: TgqeCoordinate,
pub span: TgqeSpan,
}
pub struct TgqeCoordinate {
pub filepath: String,
pub offset: u32,
pub line: i16,
pub column: i8,
}
pub struct TgqeSpan {
pub start: TgqeCoordinate,
pub end: TgqeCoordinate,
}
pub struct TgqeErrorInfo {
pub err_type: String,
pub expect: String,
pub got: String,
pub level: TgqeLevelFilter,
pub recoverable: bool,
}
pub enum TgqeLevelFilter {
Error,
Warn,
Info,
Undefined,
}As you can see, these are all plain structs rather than inscrutable generic gymnastics, so you can easily implement From for your own data structures.
For example, for chumsky::error::Rich:
use chumsky::error::Rich;
impl<T: core::fmt::Display> From<Rich<'_, T>>
for TgqeCtx
{
fn from(value: Rich<'_, T>) -> Self {
let (expect, got) = match value
.reason()
{
RichReason::ExpectedFound {
expected,
found,
} => (
expected
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(", "),
found
.as_ref()
.map(|tok| {
tok.to_string()
})
.unwrap_or_else(|| {
"end of input"
.to_string()
}),
),
RichReason::Custom(msg) => (
TGQE_DEFAULT_STR.to_string(),
msg.clone(),
),
};
let hints = value
.contexts()
.map(|(label, _)| {
label.to_string()
})
.collect::<Vec<_>>()
.join(", ");
Self::new(
unknown_position(
value.span().start as u32,
),
TgqeErrorInfo::new(
TGQE_ERRTYPE_CHUMSKY_RICH,
&expect,
&got,
TgqeLevelFilter::Error,
),
&hints,
TGQE_DEFAULT_STR,
TGQE_PUBLISHER_CHUMSKY,
0,
)
}
}You can check tgqe-golex-example for more information.
The default renderer implementation of this project is based on Ariadne. It consumes the TgqeCtx objects and outputs error messages like the following:
This project is open-sourced under the BSD-3 license.
