Skip to content

Commit

Permalink
🚧 (diagnostics): Start rewrite
Browse files Browse the repository at this point in the history
  • Loading branch information
Somfic committed Jun 14, 2024
1 parent 3ee82fd commit 4ad6e9e
Show file tree
Hide file tree
Showing 12 changed files with 574 additions and 349 deletions.
161 changes: 161 additions & 0 deletions src/diagnostic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
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,
},
}
}
}

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

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

impl Into<codespan_reporting::diagnostic::Severity> for Severity {
fn into(self) -> codespan_reporting::diagnostic::Severity {
match self {
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> Into<codespan_reporting::diagnostic::Diagnostic<&'a str>> for Diagnostic<'a> {
fn into(self) -> codespan_reporting::diagnostic::Diagnostic<&'a str> {
codespan_reporting::diagnostic::Diagnostic::<&'a str>::new(self.severity.into())
.with_labels(self.errors.into_iter().map(|error| error.into()).collect())
}
}

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

impl Into<codespan_reporting::diagnostic::LabelStyle> for Label {
fn into(self) -> codespan_reporting::diagnostic::LabelStyle {
match self {
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,
}
}
}
114 changes: 114 additions & 0 deletions src/files.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use std::collections::HashMap;

pub struct Files<'a> {
pub files: HashMap<&'a str, &'a str>,
}

impl<'a> Files<'a> {
pub fn new() -> Self {
Self {
files: HashMap::new(),
}
}

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
.get(id)
.ok_or(codespan_reporting::files::Error::FileMissing)
.copied()
}

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> {
let source = self.source(id)?;
let mut line_index = 0;
let mut byte_count = 0;

for (index, character) in source.char_indices() {
if index == byte_index {
return Ok(line_index);
}

if character == '\n' {
line_index += 1;
}

byte_count = index;
}

if byte_index == byte_count {
Ok(line_index)
} else {
Err(codespan_reporting::files::Error::IndexTooLarge {
given: byte_index,
max: byte_count,
})
}
}

fn line_range(
&'a self,
id: Self::FileId,
line_index: usize,
) -> Result<std::ops::Range<usize>, codespan_reporting::files::Error> {
let source = self.source(id)?;
let mut start = 0;
let mut end = 0;
let mut current_line_index = 0;

for (index, character) in source.char_indices() {
if current_line_index == line_index {
start = index;
}

if character == '\n' {
if current_line_index == line_index {
end = index;
break;
}

current_line_index += 1;
}
}

if current_line_index == line_index {
Ok(start..end)
} else {
Err(codespan_reporting::files::Error::IndexTooLarge {
given: line_index,
max: current_line_index,
})
}
}
}
64 changes: 20 additions & 44 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,81 +1,57 @@
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 = "
let mut files = Files::new();
files.insert(
"main",
"
enum color: red green blue;
struct person:
.name: string
.age: number
;
let 12 = 12;
// let lucas = person::new('Lucas', 22);
// lucas.age_in_days();
";
let file: SimpleFile<&str, &str> = SimpleFile::new("main", code);
",
);

let lexemes = scanner::Scanner::new(code.to_owned()).collect::<Vec<_>>();
let scanner = scanner::Scanner::new(&files);
let lexemes = scanner.parse();
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);

Label::primary((), range.position..range.position + range.length)
.with_message(diagnostic.message.to_string())
})
.collect(),
);
let diagnostics: Vec<Diagnostic<&str>> =
diagnostics.into_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
4 changes: 2 additions & 2 deletions src/parser/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ use std::collections::{HashMap, HashSet};
use crate::scanner::lexeme::Lexeme;

#[derive(Debug, Clone, PartialEq)]
pub enum Symbol {
pub enum Symbol<'a> {
Expression(Expression),
Statement(Statement),
Type(Type),
Unknown(Lexeme),
Unknown(Lexeme<'a>),
}

#[derive(Debug, Clone, PartialEq)]
Expand Down
Loading

0 comments on commit 4ad6e9e

Please sign in to comment.