Skip to content

Commit

Permalink
♻️ (diagnostics): Refactor diagnostics
Browse files Browse the repository at this point in the history
commit dd18997
Author: Lucas <[email protected]>
Date:   Sat Jun 15 14:31:12 2024 +0200

    📝 Update readme

    semver: chore

commit 5e4c2bc
Author: Lucas <[email protected]>
Date:   Fri Jun 14 21:02:51 2024 +0200

    🚨 (parser): Fix compiler warnings

    semver: chore

commit 34c9146
Author: Lucas <[email protected]>
Date:   Fri Jun 14 21:01:18 2024 +0200

    🚧 (diagnostics): Finish rewrite

commit 4ad6e9e
Author: Lucas <[email protected]>
Date:   Fri Jun 14 15:52:03 2024 +0200

    🚧 (diagnostics): Start rewrite
  • Loading branch information
Somfic committed Jun 15, 2024
1 parent 3ee82fd commit f751e82
Show file tree
Hide file tree
Showing 13 changed files with 703 additions and 523 deletions.
34 changes: 17 additions & 17 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@

> An idiot admires complexity, a genius admires simplicity.
```ts
struct person:
name: string
age: number
```rust
enum breed:
siamese
persian
maine_coon
sphynx
other ~ string

age_in_days() -> number:
let age_in_months = .age * 12;
age_in_months * 30
;
type cat:
name ~ string
age ~ number
breed ~ breed

*new(name: string, age: number) -> self:
self {
name
age
}
;
;
spec purrer:
pur ~ fn (self) -> number

let lucas = person::new('Lucas', 22);
lucas.age_in_days(); // 7920
code purrer for cat:
pur: (self) -> {
12
}
```
167 changes: 167 additions & 0 deletions src/diagnostic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
use crate::scanner::lexeme::Lexeme;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Diagnostic<'a> {
pub severity: Severity,
pub title: String,
pub errors: Vec<Error<'a>>,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Error<'a> {
pub message: String,
pub label: Label,
pub range: Range<'a>,
}

impl<'a> Error<'a> {
pub fn primary(
file_id: impl Into<&'a str>,
position: usize,
length: usize,
message: impl Into<String>,
) -> Error<'a> {
Error::new(file_id, Label::Primary, position, length, message)
}

pub fn secondary(
file_id: impl Into<&'a str>,
position: usize,
length: usize,
message: impl Into<String>,
) -> Error<'a> {
Error::new(file_id, Label::Secondary, position, length, message)
}

pub fn new(
file_id: impl Into<&'a str>,
label: Label,
position: usize,
length: usize,
message: impl Into<String>,
) -> Error<'a> {
Error {
message: message.into(),
label,
range: Range {
file_id: file_id.into(),
position,
length,
},
}
}

pub(crate) fn transform_range(mut self, lexemes: &'a [Lexeme<'a>]) -> Error {
self.range = self.range.to_source_code_range(lexemes);
self
}
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Severity {
Error,
Warning,
Note,
Help,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Label {
Primary,
Secondary,
}

impl From<Severity> for codespan_reporting::diagnostic::Severity {
fn from(val: Severity) -> Self {
match val {
Severity::Error => codespan_reporting::diagnostic::Severity::Error,
Severity::Warning => codespan_reporting::diagnostic::Severity::Warning,
Severity::Note => codespan_reporting::diagnostic::Severity::Note,
Severity::Help => codespan_reporting::diagnostic::Severity::Help,
}
}
}

impl<'a> Diagnostic<'a> {
pub fn error(message: impl Into<String>) -> Diagnostic<'a> {
Diagnostic::new(Severity::Error, message)
}

pub fn warning(message: impl Into<String>) -> Diagnostic<'a> {
Diagnostic::new(Severity::Warning, message)
}

pub fn new(severity: Severity, message: impl Into<String>) -> Diagnostic<'a> {
Diagnostic {
severity,
title: message.into(),
errors: vec![],
}
}

pub fn with_error(mut self, error: Error<'a>) -> Self {
self.errors.push(error);
self
}
}

impl<'a> From<Diagnostic<'a>> for codespan_reporting::diagnostic::Diagnostic<&'a str> {
fn from(val: Diagnostic<'a>) -> Self {
codespan_reporting::diagnostic::Diagnostic::<&'a str>::new(val.severity.into())
.with_message(val.title)
.with_labels(val.errors.into_iter().map(|error| error.into()).collect())
}
}

impl<'a> From<Error<'a>> for codespan_reporting::diagnostic::Label<&'a str> {
fn from(val: Error<'a>) -> Self {
codespan_reporting::diagnostic::Label::new(
val.label.into(),
val.range.file_id,
val.range.position..val.range.position + val.range.length,
)
.with_message(val.message)
}
}

impl From<Label> for codespan_reporting::diagnostic::LabelStyle {
fn from(val: Label) -> Self {
match val {
Label::Primary => codespan_reporting::diagnostic::LabelStyle::Primary,
Label::Secondary => codespan_reporting::diagnostic::LabelStyle::Secondary,
}
}
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Range<'a> {
pub file_id: &'a str,
pub position: usize,
pub length: usize,
}

impl<'a> Range<'a> {
pub fn to_source_code_range(self, lexemes: &[Lexeme]) -> Self {
let start = if self.position >= lexemes.len() {
let last_lexeme = lexemes[lexemes.len() - 1].range();
last_lexeme.position + 1
} else {
let start_lexeme = lexemes[self.position].range();
start_lexeme.position
};

let end = if self.position + self.length >= lexemes.len() {
let last_lexeme = lexemes[lexemes.len() - 1].range();
last_lexeme.position + last_lexeme.length
} else {
let end_lexeme = lexemes[self.position + self.length].range();
end_lexeme.position + end_lexeme.length
};

Range {
file_id: self.file_id,
position: start,
length: end - start,
}
}
}
84 changes: 84 additions & 0 deletions src/files.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use std::collections::HashMap;

#[derive(Default)]
pub struct Files<'a> {
pub files: HashMap<&'a str, &'a str>,
}

impl<'a> Files<'a> {
pub fn insert(&mut self, file_id: impl Into<&'a str>, source: impl Into<&'a str>) {
self.files.insert(file_id.into(), source.into());
}

pub fn file_ids(&self) -> Vec<&'a str> {
self.files.keys().copied().collect()
}

pub fn get(&self, file_id: impl Into<&'a str>) -> Option<&'a str> {
self.files.get(file_id.into()).copied()
}
}

impl<'a> codespan_reporting::files::Files<'a> for Files<'a> {
type FileId = &'a str;
type Name = &'a str;
type Source = &'a str;

fn name(&'a self, id: Self::FileId) -> Result<Self::Name, codespan_reporting::files::Error> {
self.files
.keys()
.find(|key| **key == id)
.copied()
.ok_or(codespan_reporting::files::Error::FileMissing)
}

fn source(
&'a self,
id: Self::FileId,
) -> Result<Self::Source, codespan_reporting::files::Error> {
self.files
.get(id)
.ok_or(codespan_reporting::files::Error::FileMissing)
.copied()
}

fn line_index(
&'a self,
id: Self::FileId,
byte_index: usize,
) -> Result<usize, codespan_reporting::files::Error> {
self.get(id)
.ok_or(codespan_reporting::files::Error::FileMissing)
.map(|source| {
source
.char_indices()
.take_while(|(index, _)| *index < byte_index)
.filter(|(_, character)| *character == '\n')
.count()
})
}

fn line_range(
&'a self,
id: Self::FileId,
line_index: usize,
) -> Result<std::ops::Range<usize>, codespan_reporting::files::Error> {
self.get(id)
.ok_or(codespan_reporting::files::Error::FileMissing)
.map(|source| {
let start = source
.lines()
.take(line_index)
.map(|line| line.len() + 1)
.sum::<usize>();

let end = source
.lines()
.take(line_index + 1)
.map(|line| line.len() + 1)
.sum::<usize>();

start..end
})
}
}
74 changes: 23 additions & 51 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,81 +1,53 @@
use anyhow::Result;
use codespan_reporting::{
diagnostic::{Diagnostic, Label},
files::SimpleFile,
diagnostic::Diagnostic,
term::{
self,
termcolor::{ColorChoice, StandardStream},
},
};
use core::result::Result::Ok;
use scanner::lexeme::{Lexeme, Range};
use files::Files;
use transpiler::{bend::BendTranspiler, Transpiler};

pub mod diagnostic;
pub mod files;
pub mod parser;
pub mod scanner;
pub mod transpiler;

fn lexeme_range_to_source_range(lexemes: &[Lexeme], diagnostic: &parser::Diagnostic) -> Range {
let start = if diagnostic.range.position >= lexemes.len() {
let last_lexeme = lexemes[lexemes.len() - 1].range();
last_lexeme.position + 1
} else {
let start_lexeme = lexemes[diagnostic.range.position].range();
start_lexeme.position
};

let end = if diagnostic.range.position + diagnostic.range.length >= lexemes.len() {
let last_lexeme = lexemes[lexemes.len() - 1].range();
last_lexeme.position + last_lexeme.length
} else {
let end_lexeme = lexemes[diagnostic.range.position + diagnostic.range.length].range();
end_lexeme.position + end_lexeme.length
};

Range {
position: start,
length: end - start,
}
}

fn main() -> Result<()> {
let code = "
enum color: red green blue;
struct person:
.name: string
.age: number
;
let mut files = Files::default();
files.insert(
"main",
"
12 + 12
",
);

// let lucas = person::new('Lucas', 22);
// lucas.age_in_days();
";
let file: SimpleFile<&str, &str> = SimpleFile::new("main", code);
let scanner = scanner::Scanner::new(&files);
let lexemes = scanner.parse();

let lexemes = scanner::Scanner::new(code.to_owned()).collect::<Vec<_>>();
let mut parser = parser::Parser::new(&lexemes);
let parsed = parser.parse();

match &parsed {
Ok(_) => {}
Err(diagnostics) => {
let diagnostic: Diagnostic<()> = Diagnostic::error()
.with_message("Syntax error")
.with_labels(
diagnostics
.iter()
.map(|diagnostic| {
let range = lexeme_range_to_source_range(&lexemes, diagnostic);
// Print the diagnostics
for diagnostic in diagnostics.iter() {
println!("{:?}", diagnostic);
}

Label::primary((), range.position..range.position + range.length)
.with_message(diagnostic.message.to_string())
})
.collect(),
);
let diagnostics: Vec<Diagnostic<&str>> =
diagnostics.iter().map(|d| d.clone().into()).collect();

let writer = StandardStream::stderr(ColorChoice::Auto);
let config = codespan_reporting::term::Config::default();
term::emit(&mut writer.lock(), &config, &file, &diagnostic)?;

for diagnostic in diagnostics {
term::emit(&mut writer.lock(), &config, &files, &diagnostic)?;
}
}
}

Expand Down
Loading

0 comments on commit f751e82

Please sign in to comment.