Most files suffered major changes

- Less ambiguous syntax
- Better parser (Chumsky only does tokenization now)
- Tidy(|ier) error handling
- Facade for simplified embedding
- External code grouped in (fairly) self-contained Systems
- Dynamic action dispatch
- Many STL additions
This commit is contained in:
2023-08-17 20:47:08 +01:00
parent 751a02a1ec
commit 3fdabc29da
139 changed files with 4269 additions and 1783 deletions

241
src/parse/errors.rs Normal file
View File

@@ -0,0 +1,241 @@
use std::rc::Rc;
use chumsky::prelude::Simple;
use itertools::Itertools;
use super::{Entry, Lexeme};
use crate::error::{ErrorPosition, ProjectError};
use crate::interner::InternedDisplay;
use crate::utils::iter::box_once;
use crate::utils::BoxedIter;
use crate::{Interner, Location, Tok};
#[derive(Debug)]
pub struct LineNeedsPrefix {
pub entry: Entry,
}
impl ProjectError for LineNeedsPrefix {
fn description(&self) -> &str {
"This linetype requires a prefix"
}
fn message(&self, i: &Interner) -> String {
format!("{} cannot appear at the beginning of a line", self.entry.bundle(i))
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { message: None, location: self.entry.location() })
}
}
#[derive(Debug)]
pub struct UnexpectedEOL {
/// Last entry before EOL
pub entry: Entry,
}
impl ProjectError for UnexpectedEOL {
fn description(&self) -> &str {
"The line ended abruptly"
}
fn message(&self, _i: &Interner) -> String {
"The line ends unexpectedly here. In Orchid, all line breaks outside \
parentheses start a new declaration"
.to_string()
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { message: None, location: self.entry.location() })
}
}
pub struct ExpectedEOL {
pub location: Location,
}
impl ProjectError for ExpectedEOL {
fn description(&self) -> &str {
"Expected the end of the line"
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.location.clone(), message: None })
}
}
#[derive(Debug)]
pub struct ExpectedName {
pub entry: Entry,
}
impl ExpectedName {
pub fn expect(entry: &Entry) -> Result<Tok<String>, Rc<dyn ProjectError>> {
match entry.lexeme {
Lexeme::Name(n) => Ok(n),
_ => Err(Self { entry: entry.clone() }.rc()),
}
}
}
impl ProjectError for ExpectedName {
fn description(&self) -> &str {
"A name was expected here, but something else was found"
}
fn message(&self, i: &Interner) -> String {
if self.entry.is_keyword() {
format!(
"{} is a restricted keyword and cannot be used as a name",
self.entry.bundle(i)
)
} else {
format!("Expected a name, found {}", self.entry.bundle(i))
}
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.entry.location(), message: None })
}
}
#[derive()]
pub struct Expected {
pub expected: Vec<Lexeme>,
pub or_name: bool,
pub found: Entry,
}
impl Expected {
pub fn expect(l: Lexeme, e: &Entry) -> Result<(), Rc<dyn ProjectError>> {
if e.lexeme != l {
return Err(
Self { expected: vec![l], or_name: false, found: e.clone() }.rc(),
);
}
Ok(())
}
}
impl ProjectError for Expected {
fn description(&self) -> &str {
"A concrete token was expected but something else was found"
}
fn message(&self, i: &Interner) -> String {
let list = match &self.expected[..] {
&[] => return "Unsatisfiable expectation".to_string(),
[only] => only.to_string_i(i),
[a, b] => format!("either {} or {}", a.bundle(i), b.bundle(i)),
[variants @ .., last] => format!(
"any of {} or {}",
variants.iter().map(|l| l.to_string_i(i)).join(", "),
last.bundle(i)
),
};
let or_name = if self.or_name { " or a name" } else { "" };
format!("Expected {}{} but found {}", list, or_name, self.found.bundle(i))
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.found.location(), message: None })
}
}
pub struct ReservedToken {
pub entry: Entry,
}
impl ProjectError for ReservedToken {
fn description(&self) -> &str {
"A token reserved for future use was found in the code"
}
fn message(&self, i: &Interner) -> String {
format!("{} is a reserved token", self.entry.bundle(i))
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.entry.location(), message: None })
}
}
pub struct BadTokenInRegion {
pub entry: Entry,
pub region: &'static str,
}
impl ProjectError for BadTokenInRegion {
fn description(&self) -> &str {
"A token was found in a region where it should not appear"
}
fn message(&self, i: &Interner) -> String {
format!("{} cannot appear in {}", self.entry.bundle(i), self.region)
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.entry.location(), message: None })
}
}
pub struct NotFound {
pub expected: &'static str,
pub location: Location,
}
impl ProjectError for NotFound {
fn description(&self) -> &str {
"A specific lexeme was expected but not found in the given range"
}
fn message(&self, _i: &Interner) -> String {
format!("{} was expected", self.expected)
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.location.clone(), message: None })
}
}
pub struct LeadingNS {
pub location: Location,
}
impl ProjectError for LeadingNS {
fn description(&self) -> &str {
":: can only follow a name token"
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.location.clone(), message: None })
}
}
pub struct MisalignedParen {
pub entry: Entry,
}
impl ProjectError for MisalignedParen {
fn description(&self) -> &str {
"Parentheses (), [] and {} must always pair up"
}
fn message(&self, i: &Interner) -> String {
format!("This {} has no pair", self.entry.bundle(i))
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.entry.location(), message: None })
}
}
pub struct NamespacedExport {
pub location: Location,
}
impl ProjectError for NamespacedExport {
fn description(&self) -> &str {
"Exports can only refer to unnamespaced names in the local namespace"
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.location.clone(), message: None })
}
}
pub struct LexError {
pub errors: Vec<Simple<char>>,
pub file: Rc<Vec<String>>,
}
impl ProjectError for LexError {
fn description(&self) -> &str {
"An error occured during tokenization"
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
let file = self.file.clone();
Box::new(self.errors.iter().map(move |s| ErrorPosition {
location: Location::Range { file: file.clone(), range: s.span() },
message: Some(format!("{}", s)),
}))
}
}