October commit

- custom parser support and infra
- type-tagging and traits (untested)
- match expressions
This commit is contained in:
2023-10-24 22:17:37 +01:00
parent c961506a3a
commit f77e4fd90a
73 changed files with 1904 additions and 558 deletions

View File

@@ -13,16 +13,27 @@ use crate::{Location, VName};
/// Hiding type parameters in associated types allows for simpler
/// parser definitions
pub trait Context {
/// Get the path to the current file
#[must_use]
fn file(&self) -> Arc<VName>;
/// Get a reference to the interner
#[must_use]
fn interner(&self) -> &Interner;
/// Get a reference to the full source text for position math and to build
/// [Location]s.
#[must_use]
fn source(&self) -> Arc<String>;
/// Get the list of all lexer plugins
#[must_use]
fn lexers(&self) -> &[&dyn LexerPlugin];
/// Get the list of all parser plugins
#[must_use]
fn line_parsers(&self) -> &[&dyn LineParser];
/// Find our position in the text given the text we've yet to parse
#[must_use]
fn pos(&self, tail: &str) -> usize { self.source().len() - tail.len() }
/// Generate a location given the length of a token and the unparsed text
/// after it. See also [Context::range_loc] if the maths gets complex.
#[must_use]
fn location(&self, len: usize, tail: &str) -> Location {
match self.pos(tail).checked_sub(len) {
@@ -33,14 +44,35 @@ pub trait Context {
},
}
}
/// Generate a location given a range in the source file. The location can be
/// computed with [Context::pos]. See also [Context::location].
#[must_use]
fn range_loc(&self, range: Range<usize>) -> Location {
Location::Range { file: self.file(), range, source: self.source() }
}
}
impl<C: Context + ?Sized> Context for &C {
fn file(&self) -> Arc<VName> { (*self).file() }
fn interner(&self) -> &Interner { (*self).interner() }
fn lexers(&self) -> &[&dyn LexerPlugin] { (*self).lexers() }
fn line_parsers(&self) -> &[&dyn LineParser] { (*self).line_parsers() }
fn location(&self, len: usize, tail: &str) -> Location {
(*self).location(len, tail)
}
fn pos(&self, tail: &str) -> usize { (*self).pos(tail) }
fn range_loc(&self, range: Range<usize>) -> Location {
(*self).range_loc(range)
}
fn source(&self) -> Arc<String> { (*self).source() }
}
/// Return value of a lexer plugin; the parsed data and the remaining string
pub type LexerPluginOut<'a> = Option<ProjectResult<(Atom, &'a str)>>;
/// Return value of a line parser; the meaningful lines derived from this parser
pub type LineParserOut = Option<ProjectResult<Vec<FileEntryKind>>>;
/// A plugin callback that reads a custom lexeme.
pub trait LexerPlugin:
for<'a> Fn(&'a str, &dyn Context) -> LexerPluginOut<'a> + Sync + Send
{
@@ -50,12 +82,17 @@ impl<F> LexerPlugin for F where
{
}
/// A plugin callback that parses a custom file entry
pub trait LineParser:
Fn(Stream<'_>, &dyn Context) -> LineParserOut + Sync + Send
for<'a> Fn(Stream<'_>, &'a (dyn Context + 'a)) -> LineParserOut
+ Sync
+ Send
{
}
impl<F> LineParser for F where
F: Fn(Stream<'_>, &dyn Context) -> LineParserOut + Sync + Send
F: for<'a> Fn(Stream<'_>, &'a (dyn Context + 'a)) -> LineParserOut
+ Sync
+ Send
{
}
@@ -64,22 +101,23 @@ impl<F> LineParser for F where
/// Hiding type parameters in associated types allows for simpler
/// parser definitions
pub struct ParsingContext<'a> {
pub interner: &'a Interner,
pub file: Arc<VName>,
pub source: Arc<String>,
pub lexers: &'a [&'a dyn LexerPlugin],
pub line_parsers: &'a [&'a dyn LineParser],
interner: &'a Interner,
file_path: Arc<VName>,
source: Arc<String>,
lexers: &'a [&'a dyn LexerPlugin],
line_parsers: &'a [&'a dyn LineParser],
}
impl<'a> ParsingContext<'a> {
/// Create a new parsing context
pub fn new(
interner: &'a Interner,
file: Arc<VName>,
file_path: Arc<VName>,
source: Arc<String>,
lexers: &'a [&'a dyn LexerPlugin],
line_parsers: &'a [&'a dyn LineParser],
) -> Self {
Self { interner, file, source, lexers, line_parsers }
Self { interner, file_path, source, lexers, line_parsers }
}
}
@@ -87,7 +125,7 @@ impl<'a> Clone for ParsingContext<'a> {
fn clone(&self) -> Self {
Self {
interner: self.interner,
file: self.file.clone(),
file_path: self.file_path.clone(),
source: self.source.clone(),
lexers: self.lexers,
line_parsers: self.line_parsers,
@@ -97,7 +135,7 @@ impl<'a> Clone for ParsingContext<'a> {
impl Context for ParsingContext<'_> {
fn interner(&self) -> &Interner { self.interner }
fn file(&self) -> Arc<VName> { self.file.clone() }
fn file(&self) -> Arc<VName> { self.file_path.clone() }
fn source(&self) -> Arc<String> { self.source.clone() }
fn lexers(&self) -> &[&dyn LexerPlugin] { self.lexers }
fn line_parsers(&self) -> &[&dyn LineParser] { self.line_parsers }
@@ -116,3 +154,23 @@ impl<'a> Context for MockContext<'a> {
fn range_loc(&self, _: Range<usize>) -> Location { Location::Unknown }
fn source(&self) -> Arc<String> { Arc::new(String::new()) }
}
pub struct FlatLocContext<'a, C: Context + ?Sized> {
sub: &'a C,
location: &'a Location,
}
impl<'a, C: Context + ?Sized> FlatLocContext<'a, C> {
pub fn new(sub: &'a C, location: &'a Location) -> Self {
Self { sub, location }
}
}
impl<'a, C: Context + ?Sized> Context for FlatLocContext<'a, C> {
fn interner(&self) -> &Interner { self.sub.interner() }
fn pos(&self, _: &str) -> usize { 0 }
fn file(&self) -> Arc<VName> { self.sub.file() }
fn lexers(&self) -> &[&dyn LexerPlugin] { self.sub.lexers() }
fn line_parsers(&self) -> &[&dyn LineParser] { self.sub.line_parsers() }
fn source(&self) -> Arc<String> { self.sub.source() }
fn location(&self, _: usize, _: &str) -> Location { self.location.clone() }
fn range_loc(&self, _: Range<usize>) -> Location { self.location.clone() }
}