use std::sync::Arc;
use gramatika::{Parse, ParseStreamer, Span, Spanned, SpannedError, Token as _};
use crate::{
parser::ErrorRecoveringParseStream,
token::{brace, directive, ident, punct, Token, TokenKind},
ParseStream,
};
#[derive(Clone, DebugLisp)]
pub struct ImportPathDecl {
pub keyword: Token,
pub path: ImportPath,
}
#[derive(Clone, DebugLisp)]
pub struct ImportDecl {
pub keyword: Token,
pub path: ImportPath,
}
#[derive(Clone, DebugLisp)]
pub enum ImportPath {
Namespaced(NamespacedImportPath),
Block(ImportPathBlock),
Leaf(ImportPathLeaf),
}
#[derive(Clone, DebugLisp)]
pub struct NamespacedImportPath {
pub namespace: Token,
pub path: Arc<ImportPath>,
}
#[derive(Clone, DebugLisp)]
pub struct ImportPathBlock {
pub brace_open: Token,
pub paths: Arc<[ImportPath]>,
pub brace_close: Token,
}
#[derive(Clone, DebugLisp)]
pub struct ImportPathLeaf {
pub name: Token,
pub as_binding: Option<Token>,
}
impl ImportPathDecl {
pub fn name(&self) -> &Token {
let mut import_path = &self.path;
loop {
match import_path {
ImportPath::Namespaced(NamespacedImportPath { path, .. }) => {
import_path = path.as_ref();
}
ImportPath::Leaf(ImportPathLeaf { name, .. }) => {
break name;
}
ImportPath::Block(_) => {
unreachable!();
}
}
}
}
}
impl Parse for ImportPathDecl {
type Stream = ParseStream;
fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
let keyword = input.consume(directive!["#define_import_path"])?;
let path = input.parse()?;
let mut parsed_path = &path;
loop {
match parsed_path {
ImportPath::Namespaced(NamespacedImportPath { path, .. }) => {
parsed_path = path.as_ref();
}
ImportPath::Block(block) => {
return Err(SpannedError {
message: "Path blocks are not valid in `#define_import_path`".into(),
source: input.source(),
span: Some(block.span()),
});
}
ImportPath::Leaf(leaf) => {
if let Some(as_binding) = leaf.as_binding.as_ref() {
return Err(SpannedError {
message: "`as` bindings are not valid in `#define_import_path`".into(),
source: input.source(),
span: Some(as_binding.span()),
});
} else {
break;
}
}
}
}
Ok(Self { keyword, path })
}
}
impl Spanned for ImportPathDecl {
fn span(&self) -> Span {
self.keyword.span().through(self.path.span())
}
}
impl Parse for ImportDecl {
type Stream = ParseStream;
fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
let keyword = input.consume(directive!["#import"])?;
let path = input.parse()?;
Ok(Self { keyword, path })
}
}
impl Spanned for ImportDecl {
fn span(&self) -> Span {
self.keyword.span().through(self.path.span())
}
}
impl Parse for ImportPath {
type Stream = ParseStream;
fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
use TokenKind::*;
match input.next() {
Some(mut next) => match next.as_matchable() {
(Ident | Path, _, _) => {
next = match next.kind() {
TokenKind::Ident => input.upgrade_last(TokenKind::Ident, Token::module)?,
TokenKind::Path => next,
_ => unreachable!(),
};
if input.check(punct![::]) {
input.discard();
Ok(ImportPath::Namespaced(NamespacedImportPath {
namespace: next,
path: Arc::new(input.parse()?),
}))
} else {
let name = next;
let as_binding = if input.check(ident![as]) {
let _ = input.consume_as(TokenKind::Ident, Token::keyword)?;
Some(input.consume_as(TokenKind::Ident, Token::module)?)
} else {
None
};
Ok(ImportPath::Leaf(ImportPathLeaf { name, as_binding }))
}
}
(Brace, "{", _) => {
let brace_open = next;
let paths =
input.parse_seq_separated(punct![,], |input| !input.check(brace!("}")))?;
let brace_close = input.consume(brace!("}"))?;
Ok(ImportPath::Block(ImportPathBlock {
brace_open,
paths: paths.into(),
brace_close,
}))
}
(_, _, span) => Err(SpannedError {
message: "Expected identifier or `{`".into(),
source: input.source(),
span: Some(span),
}),
},
None => Err(SpannedError {
message: "Unexpected end of input".into(),
source: input.source(),
span: input.prev().map(|token| token.span()),
}),
}
}
}
impl Spanned for ImportPath {
fn span(&self) -> Span {
match self {
ImportPath::Namespaced(NamespacedImportPath { namespace, path }) => {
namespace.span().through(path.span())
}
ImportPath::Block(inner) => inner.span(),
ImportPath::Leaf(inner) => inner.span(),
}
}
}
impl Spanned for ImportPathBlock {
fn span(&self) -> Span {
self.brace_open.span().through(self.brace_close.span())
}
}
impl Spanned for ImportPathLeaf {
fn span(&self) -> Span {
if let Some(binding) = self.as_binding.as_ref() {
self.name.span().through(binding.span())
} else {
self.name.span()
}
}
}