dolang-compile 0.1.0

Parser, resolver, and bytecode compiler for the Do language.
Documentation
use std::{
    cell::OnceCell,
    fmt::{self, Debug, Write},
    ops::{BitOr, Range},
    path::{Path, PathBuf},
};

use crate::diag::{self, Annotation, AnnotationKind, NoteKind, Pos, Severity};

use dolang_util::arena::ArenaVec;

use super::Compiler;

pub(crate) type Offset = u32;

#[derive(Copy, Clone, PartialEq, Eq, Default, Hash)]
pub struct Span {
    pub start: Offset,
    pub end: Offset,
}

impl Debug for Span {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}..{}", self.start, self.end)
    }
}

impl Span {
    pub(crate) const INVALID: Span = Span {
        start: Offset::MAX,
        end: Offset::MIN,
    };

    pub(crate) fn left_char(&self) -> Self {
        if self.start == self.end {
            Self {
                start: self.start,
                end: self.end,
            }
        } else {
            Self {
                start: self.start,
                end: self.start + 1,
            }
        }
    }

    pub(crate) fn right_char(&self) -> Self {
        if self.start == self.end {
            Self {
                start: self.start,
                end: self.end,
            }
        } else {
            Self {
                start: self.end - 1,
                end: self.end,
            }
        }
    }

    pub(crate) fn after_right_char(&self) -> Self {
        Self {
            start: self.end,
            end: self.end + 1,
        }
    }

    pub(crate) fn before_left_char(&self) -> Self {
        Self {
            start: self.start - 1,
            end: self.start,
        }
    }
}

impl BitOr<Span> for &Span {
    type Output = Span;

    fn bitor(self, other: Span) -> Span {
        Span {
            start: self.start.min(other.start),
            end: self.end.max(other.end),
        }
    }
}

impl BitOr<&Span> for &Span {
    type Output = Span;

    fn bitor(self, other: &Span) -> Span {
        Span {
            start: self.start.min(other.start),
            end: self.end.max(other.end),
        }
    }
}

impl BitOr<&Span> for Span {
    type Output = Span;

    fn bitor(self, other: &Self) -> Span {
        Span {
            start: self.start.min(other.start),
            end: self.end.max(other.end),
        }
    }
}

impl BitOr<Span> for Span {
    type Output = Span;

    fn bitor(self, other: Self) -> Span {
        Span {
            start: self.start.min(other.start),
            end: self.end.max(other.end),
        }
    }
}

impl<T: Into<Offset>> From<Range<T>> for Span {
    fn from(value: Range<T>) -> Self {
        Span {
            start: value.start.into(),
            end: value.end.into(),
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) struct Coord {
    pub line: Offset,
    pub column: Offset,
}

impl From<(Offset, Offset)> for Coord {
    fn from((line, column): (Offset, Offset)) -> Self {
        Coord { line, column }
    }
}

#[derive(Clone)]
pub(crate) struct File<'s> {
    path: PathBuf,
    content: &'s [u8],
    newlines: OnceCell<Vec<Offset>>,
}

impl<'s> File<'s> {
    pub(crate) fn new(path: &Path, content: &'s [u8]) -> Self {
        File {
            path: path.to_owned(),
            content,
            newlines: Default::default(),
        }
    }

    pub(crate) fn content(&self) -> &'s [u8] {
        self.content
    }

    pub(crate) fn slice(&self, span: Span) -> &'s [u8] {
        &self.content[span.start as usize..span.end as usize]
    }

    pub(crate) fn str(&self, span: Span) -> &'s str {
        str::from_utf8(self.slice(span)).expect("invalid utf-8")
    }

    fn newlines(&self) -> &[Offset] {
        self.newlines.get_or_init(|| {
            let mut newlines: Vec<Offset> = Default::default();
            let mut iter = self.content.iter();
            let mut cur = 0usize;

            while let Some(pos) = iter.position(|&c| c == b'\n') {
                newlines.push(Offset::try_from(cur + pos).unwrap());
                cur += pos + 1;
            }

            newlines
        })
    }

    pub(crate) fn coord(&self, offset: Offset) -> Coord {
        let newlines = self.newlines();
        let index = newlines.partition_point(|&o| offset > o);
        if index == 0 {
            Coord {
                line: 0,
                column: offset,
            }
        } else {
            Coord {
                line: index as u32,
                column: offset - newlines[index - 1] - 1,
            }
        }
    }

    pub(crate) fn coord_span(&self, span: Span) -> Range<Coord> {
        self.coord(span.start)..self.coord(span.end)
    }

    pub(crate) fn path(&self) -> &Path {
        &self.path
    }
}

pub(crate) trait Annotate {
    fn kind(&self) -> AnnotationKind;
    fn span(&self) -> Span;
    fn message(&self, compiler: &Compiler<'_>, w: &mut dyn Write) -> fmt::Result;
}

pub(crate) trait Note {
    fn kind(&self) -> NoteKind;
    fn message(&self, compiler: &Compiler<'_>, w: &mut dyn Write) -> fmt::Result;
}

pub(crate) trait Patch {
    fn span(&self) -> Span;
    fn message(&self, compiler: &Compiler<'_>, w: &mut dyn Write) -> fmt::Result;
    fn sub(&self, compiler: &Compiler<'_>, w: &mut dyn Write) -> fmt::Result;
}

pub(crate) trait Diagnose {
    fn span(&self) -> Span;
    fn severity(&self) -> Severity;
    fn message(&self, compiler: &Compiler<'_>, w: &mut dyn Write) -> fmt::Result;

    fn annotations(&self) -> Box<dyn Iterator<Item = Box<dyn Annotate>>> {
        Box::new([].into_iter())
    }

    fn notes(&self) -> Box<dyn Iterator<Item = Box<dyn Note>>> {
        Box::new([].into_iter())
    }

    fn patches(&self) -> Box<dyn Iterator<Item = Box<dyn Patch>>> {
        Box::new([].into_iter())
    }
}

pub struct Diag(Box<dyn Diagnose>);

impl Diag {
    pub(crate) fn new(info: impl Diagnose + 'static) -> Self {
        Self(Box::new(info))
    }

    fn resolve_span(compiler: &Compiler<'_>, span: Span) -> diag::Span {
        let coords = compiler.file.coord_span(span);
        diag::Span::new(
            Pos::new(span.start as usize, coords.start.line, coords.start.column),
            Pos::new(span.end as usize, coords.end.line, coords.end.column),
        )
    }

    pub(crate) fn resolve<'a>(&self, compiler: &Compiler<'a>) -> diag::Diag {
        let span = Self::resolve_span(compiler, self.0.span());
        let mut msg = String::new();
        self.0.message(compiler, &mut msg).unwrap();
        diag::Diag::new(
            self.0.severity(),
            span,
            msg,
            self.0.annotations().map(|a| {
                let span = Self::resolve_span(compiler, a.span());
                let mut message = String::new();
                a.message(compiler, &mut message).unwrap();
                Annotation {
                    kind: a.kind(),
                    span,
                    message,
                }
            }),
            self.0.notes().map(|n| {
                let mut message = String::new();
                n.message(compiler, &mut message).unwrap();
                diag::Note {
                    kind: n.kind(),
                    message,
                }
            }),
            self.0.patches().map(|p| {
                let span = Self::resolve_span(compiler, p.span());
                let mut sub = String::new();
                p.sub(compiler, &mut sub).unwrap();
                let mut message = String::new();
                p.message(compiler, &mut message).unwrap();
                diag::Patch { span, sub, message }
            }),
        )
    }
}

pub(crate) struct Diags {
    vec: ArenaVec<Diag>,
}

impl Diags {
    pub(crate) fn new() -> Self {
        Self {
            vec: ArenaVec::new(),
        }
    }

    pub(crate) fn push(&self, info: impl Diagnose + 'static) {
        self.vec.push(Diag::new(info))
    }

    pub(crate) fn drain(&mut self) -> impl Iterator<Item = Diag> {
        self.vec.drain()
    }
}