Backup commit

My backspace key started ghosting. Nothing works atm.
This commit is contained in:
2024-01-27 14:50:33 +00:00
parent f77e4fd90a
commit a8887227e5
236 changed files with 10946 additions and 8977 deletions

View File

@@ -1,176 +1,127 @@
//! Definition and implementations of the parsing context, which is used
use std::ops::Range;
use std::sync::Arc;
use super::stream::Stream;
use crate::error::ProjectResult;
use crate::foreign::Atom;
use crate::interner::Interner;
use crate::sourcefile::FileEntryKind;
use crate::{Location, VName};
use super::lex_plugin::LexerPlugin;
use super::parse_plugin::ParseLinePlugin;
use crate::location::{SourceCode, SourceRange};
use crate::name::VPath;
use crate::utils::boxed_iter::{box_empty, BoxedIter};
use crate::utils::sequence::Sequence;
/// Trait enclosing all context features
///
/// Hiding type parameters in associated types allows for simpler
/// parser definitions
pub trait Context {
/// Get the path to the current file
/// The main implementation is [ParsingContext]
pub trait ParseCtx {
/// Get an object describing the file this source code comes from
#[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>;
fn code_info(&self) -> SourceCode;
/// Get the list of all lexer plugins
#[must_use]
fn lexers(&self) -> &[&dyn LexerPlugin];
fn lexers(&self) -> BoxedIter<'_, &dyn LexerPlugin>;
/// Get the list of all parser plugins
#[must_use]
fn line_parsers(&self) -> &[&dyn LineParser];
fn line_parsers(&self) -> BoxedIter<'_, &dyn ParseLinePlugin>;
/// 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) {
Some(start) => self.range_loc(start..self.pos(tail)),
fn range(&self, len: usize, tl: &str) -> Range<usize> {
match self.pos(tl).checked_sub(len) {
Some(start) => start..self.pos(tl),
None => {
let tl = tail.len();
panic!("len={len} greater than tail.len()={tl}; tail={tail:?}")
panic!("len={len} greater than tail.len()={}; tail={tl:?}", tl.len())
},
}
}
/// Generate a location given a range in the source file. The location can be
/// computed with [Context::pos]. See also [Context::location].
/// Create a contextful location for error reporting
#[must_use]
fn range_loc(&self, range: Range<usize>) -> Location {
Location::Range { file: self.file(), range, source: self.source() }
fn code_range(&self, len: usize, tl: &str) -> SourceRange {
self.range_loc(&self.range(len, tl))
}
/// Create a contentful location from a range directly.
#[must_use]
fn range_loc(&self, range: &Range<usize>) -> SourceRange {
SourceRange { code: self.code_info(), range: range.clone() }
}
/// Get a reference to the full source text. This should not be used for
/// position math.
#[must_use]
fn source(&self) -> Arc<String> { self.code_info().source.clone() }
}
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)
impl<'a, C: ParseCtx + 'a + ?Sized> ParseCtx for &'a C {
fn lexers(&self) -> BoxedIter<'_, &dyn LexerPlugin> { (*self).lexers() }
fn line_parsers(&self) -> BoxedIter<'_, &dyn ParseLinePlugin> {
(*self).line_parsers()
}
fn pos(&self, tail: &str) -> usize { (*self).pos(tail) }
fn range_loc(&self, range: Range<usize>) -> Location {
(*self).range_loc(range)
}
fn code_info(&self) -> SourceCode { (*self).code_info() }
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
{
}
impl<F> LexerPlugin for F where
F: for<'a> Fn(&'a str, &dyn Context) -> LexerPluginOut<'a> + Sync + Send
{
}
/// A plugin callback that parses a custom file entry
pub trait LineParser:
for<'a> Fn(Stream<'_>, &'a (dyn Context + 'a)) -> LineParserOut
+ Sync
+ Send
{
}
impl<F> LineParser for F where
F: for<'a> Fn(Stream<'_>, &'a (dyn Context + 'a)) -> LineParserOut
+ Sync
+ Send
{
fn range(&self, l: usize, t: &str) -> Range<usize> { (*self).range(l, t) }
}
/// Struct implementing context
///
/// Hiding type parameters in associated types allows for simpler
/// parser definitions
pub struct ParsingContext<'a> {
interner: &'a Interner,
file_path: Arc<VName>,
source: Arc<String>,
lexers: &'a [&'a dyn LexerPlugin],
line_parsers: &'a [&'a dyn LineParser],
#[derive(Clone)]
pub struct ParseCtxImpl<'a> {
/// File to be parsed; where it belongs in the tree and its text
pub code: SourceCode,
/// Lexer plugins for parsing custom literals
pub lexers: Sequence<'a, &'a (dyn LexerPlugin + 'a)>,
/// Parser plugins for parsing custom line structures
pub line_parsers: Sequence<'a, &'a dyn ParseLinePlugin>,
}
impl<'a> ParsingContext<'a> {
/// Create a new parsing context
pub fn new(
interner: &'a Interner,
file_path: Arc<VName>,
source: Arc<String>,
lexers: &'a [&'a dyn LexerPlugin],
line_parsers: &'a [&'a dyn LineParser],
) -> Self {
Self { interner, file_path, source, lexers, line_parsers }
impl<'a> ParseCtx for ParseCtxImpl<'a> {
// Rust doesn't realize that this lifetime is covariant
#[allow(clippy::map_identity)]
fn lexers(&self) -> BoxedIter<'_, &dyn LexerPlugin> {
Box::new(self.lexers.iter().map(|r| r))
}
}
impl<'a> Clone for ParsingContext<'a> {
fn clone(&self) -> Self {
Self {
interner: self.interner,
file_path: self.file_path.clone(),
source: self.source.clone(),
lexers: self.lexers,
line_parsers: self.line_parsers,
}
#[allow(clippy::map_identity)]
fn line_parsers(&self) -> BoxedIter<'_, &dyn ParseLinePlugin> {
Box::new(self.line_parsers.iter().map(|r| r))
}
fn code_info(&self) -> SourceCode { self.code.clone() }
}
impl Context for ParsingContext<'_> {
fn interner(&self) -> &Interner { self.interner }
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 }
}
pub struct MockContext<'a>(pub &'a Interner);
impl<'a> Context for MockContext<'a> {
// these are doing something
fn interner(&self) -> &Interner { self.0 }
/// Context instance for testing
pub struct MockContext;
impl ParseCtx for MockContext {
fn pos(&self, tail: &str) -> usize { usize::MAX / 2 - tail.len() }
// these are expendable
fn file(&self) -> Arc<VName> { Arc::new(Vec::new()) }
fn lexers(&self) -> &[&dyn LexerPlugin] { &[] }
fn line_parsers(&self) -> &[&dyn LineParser] { &[] }
fn location(&self, _: usize, _: &str) -> Location { Location::Unknown }
fn range_loc(&self, _: Range<usize>) -> Location { Location::Unknown }
fn source(&self) -> Arc<String> { Arc::new(String::new()) }
fn code_info(&self) -> SourceCode {
SourceCode {
path: Arc::new(VPath(vec![])),
source: Arc::new(String::new()),
}
}
fn lexers(&self) -> BoxedIter<'_, &dyn LexerPlugin> { box_empty() }
fn line_parsers(&self) -> BoxedIter<'_, &dyn ParseLinePlugin> { box_empty() }
}
pub struct FlatLocContext<'a, C: Context + ?Sized> {
/// Context that assigns the same location to every subset of the source code.
/// Its main use case is to process source code that was dynamically generated
/// in response to some user code.
pub struct FlatLocContext<'a, C: ParseCtx + ?Sized> {
sub: &'a C,
location: &'a Location,
range: &'a SourceRange,
}
impl<'a, C: Context + ?Sized> FlatLocContext<'a, C> {
pub fn new(sub: &'a C, location: &'a Location) -> Self {
Self { sub, location }
impl<'a, C: ParseCtx + ?Sized> FlatLocContext<'a, C> {
/// Create a new context that will use the same provided range for every
/// parsed token
pub fn new(sub: &'a C, range: &'a SourceRange) -> Self { Self { sub, range } }
}
impl<'a, C: ParseCtx + ?Sized> ParseCtx for FlatLocContext<'a, C> {
fn pos(&self, _: &str) -> usize { 0 }
fn lexers(&self) -> BoxedIter<'_, &dyn LexerPlugin> { self.sub.lexers() }
fn line_parsers(&self) -> BoxedIter<'_, &dyn ParseLinePlugin> {
self.sub.line_parsers()
}
fn code_info(&self) -> SourceCode { self.range.code.clone() }
fn range(&self, _: usize, _: &str) -> Range<usize> {
self.range.range.clone()
}
}
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() }
}