Files
orchid/src/parse/multiname.rs
Lawrence Bethlenfalvy 3c0056c2db Generic mutation scheduling system
IO adapted to use it
Also, Atoms can now dispatch type-erased requests
2023-09-14 22:54:42 +01:00

129 lines
3.6 KiB
Rust

use std::collections::VecDeque;
use super::context::Context;
use super::errors::Expected;
use super::stream::Stream;
use super::Lexeme;
use crate::error::{ProjectError, ProjectResult};
use crate::sourcefile::Import;
use crate::utils::boxed_iter::{box_chain, box_once};
use crate::utils::BoxedIter;
use crate::{Location, Tok};
struct Subresult {
glob: bool,
deque: VecDeque<Tok<String>>,
location: Location,
}
impl Subresult {
fn new_glob(location: Location) -> Self {
Self { glob: true, deque: VecDeque::new(), location }
}
fn new_named(name: Tok<String>, location: Location) -> Self {
Self { location, glob: false, deque: VecDeque::from([name]) }
}
fn push_front(mut self, name: Tok<String>) -> Self {
self.deque.push_front(name);
self
}
fn finalize(self) -> Import {
let Self { mut deque, glob, location } = self;
debug_assert!(glob || !deque.is_empty(), "The constructors forbid this");
let name = if glob { None } else { deque.pop_back() };
Import { name, location, path: deque.into() }
}
}
fn parse_multiname_branch(
cursor: Stream<'_>,
ctx: impl Context,
) -> ProjectResult<(BoxedIter<Subresult>, Stream<'_>)> {
let comma = ctx.interner().i(",");
let (subnames, cursor) = parse_multiname_rec(cursor, ctx.clone())?;
let (delim, cursor) = cursor.trim().pop()?;
match &delim.lexeme {
Lexeme::Name(n) if n == &comma => {
let (tail, cont) = parse_multiname_branch(cursor, ctx)?;
Ok((box_chain!(subnames, tail), cont))
},
Lexeme::RP('(') => Ok((subnames, cursor)),
_ => Err(
Expected {
expected: vec![Lexeme::Name(comma), Lexeme::RP('(')],
or_name: false,
found: delim.clone(),
}
.rc(),
),
}
}
fn parse_multiname_rec(
curosr: Stream<'_>,
ctx: impl Context,
) -> ProjectResult<(BoxedIter<Subresult>, Stream<'_>)> {
let star = ctx.interner().i("*");
let comma = ctx.interner().i(",");
let (head, mut cursor) = curosr.trim().pop()?;
match &head.lexeme {
Lexeme::LP('(') => parse_multiname_branch(cursor, ctx),
Lexeme::LP('[') => {
let mut names = Vec::new();
loop {
let head;
(head, cursor) = cursor.trim().pop()?;
match &head.lexeme {
Lexeme::Name(n) => names.push((n, head.location())),
Lexeme::RP('[') => break,
_ => {
let err = Expected {
expected: vec![Lexeme::RP('[')],
or_name: true,
found: head.clone(),
};
return Err(err.rc());
},
}
}
Ok((
Box::new(names.into_iter().map(|(name, location)| {
Subresult::new_named(name.clone(), location)
})),
cursor,
))
},
Lexeme::Name(n) if *n == star =>
Ok((box_once(Subresult::new_glob(head.location())), cursor)),
Lexeme::Name(n) if ![comma, star].contains(n) => {
let cursor = cursor.trim();
if cursor.get(0).ok().map(|e| &e.lexeme) == Some(&Lexeme::NS) {
let cursor = cursor.step()?;
let (out, cursor) = parse_multiname_rec(cursor, ctx)?;
let out = Box::new(out.map(|sr| sr.push_front(n.clone())));
Ok((out, cursor))
} else {
Ok((box_once(Subresult::new_named(n.clone(), head.location())), cursor))
}
},
_ => Err(
Expected {
expected: vec![Lexeme::LP('(')],
or_name: true,
found: head.clone(),
}
.rc(),
),
}
}
pub fn parse_multiname(
cursor: Stream<'_>,
ctx: impl Context,
) -> ProjectResult<(Vec<Import>, Stream<'_>)> {
let (output, cont) = parse_multiname_rec(cursor, ctx)?;
Ok((output.map(|sr| sr.finalize()).collect(), cont))
}