tamer: asg::air::ir::AirPkg::PkgImport: New token
This allows us to drop `AirIdent::IdentRef`, which in turn allows dropping `AirIdent` entirely from `AirPkgAggregate`. This is also a more appropriate abstraction; having to track all the ways in which `IdentRef` was used can be confusing. This means that `AirIdent` is true to its name---used only for identifiers. The new token type makes it very clear where package imports are recognized, and it's also easier to search for. DEV-13162main
parent
1f2315436c
commit
896fb3a0e5
|
@ -202,8 +202,10 @@ impl ParseState for AirAggregate {
|
|||
tok @ AirPkg(..),
|
||||
) => ctx.ret_or_transfer(st, tok, AirPkgAggregate::new()),
|
||||
(Pkg(pkg), AirPkg(etok)) => ctx.proxy(pkg, etok),
|
||||
(Pkg(pkg), AirBind(etok)) => ctx.proxy(pkg, etok),
|
||||
(Pkg(pkg), AirDoc(etok)) => ctx.proxy(pkg, etok),
|
||||
(st @ Pkg(..), tok @ AirBind(_)) => {
|
||||
ctx.try_ret_with_lookahead(st, tok)
|
||||
}
|
||||
|
||||
// Expression
|
||||
(st @ (Pkg(_) | PkgTpl(_)), tok @ AirExpr(..)) => {
|
||||
|
|
|
@ -482,6 +482,19 @@ sum_ir! {
|
|||
display: |f| write!(f, "open package"),
|
||||
},
|
||||
|
||||
/// Import a package identified by the provided namespec.
|
||||
///
|
||||
/// This is similar to [`AirBind::RefIdent`],
|
||||
/// except that this is used for package references whereas
|
||||
/// the latter is used for identifiers.
|
||||
/// Having a token to uniquely represent imports allows package
|
||||
/// parsers to omit support for [`AirBind`] entirely rather
|
||||
/// than supporting that subset only to use the one token.
|
||||
PkgImport(namespec: SPair) => {
|
||||
span: namespec,
|
||||
display: |f| write!(f, "import package {}", TtQuote::wrap(namespec)),
|
||||
},
|
||||
|
||||
/// Complete processing of the current package.
|
||||
PkgEnd(span: Span) => {
|
||||
span: span,
|
||||
|
@ -827,13 +840,9 @@ sum_ir! {
|
|||
}
|
||||
}
|
||||
|
||||
/// Package definitions.
|
||||
///
|
||||
/// It is assumed that tokens that may appear as the body of a package,
|
||||
/// with the exception of [`AirIdent`],
|
||||
/// will preempt the package parser,
|
||||
/// and so are not included here.
|
||||
pub sum enum AirBindablePkg = AirPkg | AirBind | AirDoc;
|
||||
/// Package definitions interspersed with documentation in a
|
||||
/// literate style.
|
||||
pub sum enum AirLiteratePkg = AirPkg | AirDoc;
|
||||
|
||||
/// Expressions that are able to be bound to identifiers.
|
||||
///
|
||||
|
@ -848,6 +857,17 @@ sum_ir! {
|
|||
pub sum enum AirBindableMeta = AirMeta | AirBind;
|
||||
}
|
||||
|
||||
impl AirBind {
|
||||
/// Name of the identifier described by this token.
|
||||
pub fn name(&self) -> SPair {
|
||||
use AirBind::*;
|
||||
|
||||
match self {
|
||||
BindIdent(name) | RefIdent(name) => *name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AirIdent {
|
||||
/// Name of the identifier described by this token.
|
||||
pub fn name(&self) -> SPair {
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
use super::{
|
||||
super::{graph::object::Pkg, AsgError, ObjectIndex},
|
||||
ir::AirBindablePkg,
|
||||
ir::AirLiteratePkg,
|
||||
AirAggregate, AirAggregateCtx,
|
||||
};
|
||||
use crate::{diagnose::Annotate, diagnostic_todo, parse::prelude::*};
|
||||
|
@ -59,7 +59,7 @@ impl Display for AirPkgAggregate {
|
|||
}
|
||||
|
||||
impl ParseState for AirPkgAggregate {
|
||||
type Token = AirBindablePkg;
|
||||
type Token = AirLiteratePkg;
|
||||
type Object = ();
|
||||
type Error = AsgError;
|
||||
type Context = AirAggregateCtx;
|
||||
|
@ -70,8 +70,8 @@ impl ParseState for AirPkgAggregate {
|
|||
tok: Self::Token,
|
||||
ctx: &mut Self::Context,
|
||||
) -> crate::parse::TransitionResult<Self::Super> {
|
||||
use super::ir::{AirBind::*, AirDoc::*, AirPkg::*};
|
||||
use AirBindablePkg::*;
|
||||
use super::ir::{AirDoc::*, AirPkg::*};
|
||||
use AirLiteratePkg::*;
|
||||
use AirPkgAggregate::*;
|
||||
|
||||
match (self, tok) {
|
||||
|
@ -94,11 +94,6 @@ impl ParseState for AirPkgAggregate {
|
|||
}
|
||||
}
|
||||
|
||||
(Toplevel(oi_pkg), AirBind(BindIdent(name))) => {
|
||||
Transition(Toplevel(oi_pkg))
|
||||
.err(AsgError::InvalidBindContext(name))
|
||||
}
|
||||
|
||||
(Toplevel(oi_pkg), AirPkg(PkgEnd(span))) => {
|
||||
oi_pkg.close(ctx.asg_mut(), span);
|
||||
ctx.pkg_clear();
|
||||
|
@ -123,19 +118,21 @@ impl ParseState for AirPkgAggregate {
|
|||
}
|
||||
|
||||
// Package import
|
||||
(Toplevel(oi_pkg), AirBind(RefIdent(pathspec))) => oi_pkg
|
||||
.import(ctx.asg_mut(), pathspec)
|
||||
(Toplevel(oi_pkg), AirPkg(PkgImport(namespec))) => oi_pkg
|
||||
.import(ctx.asg_mut(), namespec)
|
||||
.map(|_| ())
|
||||
.transition(Toplevel(oi_pkg)),
|
||||
|
||||
(Ready, AirPkg(PkgImport(namespec))) => {
|
||||
Transition(Ready).err(AsgError::InvalidPkgImport(namespec))
|
||||
}
|
||||
|
||||
(Ready, AirPkg(PkgEnd(span))) => {
|
||||
Transition(Ready).err(AsgError::InvalidPkgEndContext(span))
|
||||
}
|
||||
|
||||
// Token may refer to a parent context.
|
||||
(st @ Ready, tok @ (AirBind(..) | AirDoc(..))) => {
|
||||
Transition(st).dead(tok)
|
||||
}
|
||||
(st @ Ready, tok @ AirDoc(..)) => Transition(st).dead(tok),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -525,36 +525,6 @@ fn pkg_cannot_redeclare() {
|
|||
assert_eq!(S6.merge(S8).unwrap(), oi_pkg.resolve(ctx.asg_ref()).span());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pkg_cannot_rename() {
|
||||
let pkg_name = SPair("/foo/bar".into(), S1);
|
||||
let name = SPair("baz".into(), S2);
|
||||
|
||||
#[rustfmt::skip]
|
||||
let toks = vec![
|
||||
PkgStart(S1, pkg_name),
|
||||
BindIdent(name),
|
||||
PkgEnd(S3),
|
||||
];
|
||||
|
||||
let sut = Sut::parse(toks.into_iter());
|
||||
|
||||
assert_eq!(
|
||||
#[rustfmt::skip]
|
||||
vec![
|
||||
Ok(Incomplete), // PkgStart
|
||||
|
||||
Err(ParseError::StateError(
|
||||
AsgError::InvalidBindContext(name),
|
||||
)),
|
||||
|
||||
// RECOVERY
|
||||
Ok(Incomplete), // PkgEnd
|
||||
],
|
||||
sut.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pkg_import_canonicalized_against_current_pkg() {
|
||||
let pkg_name = SPair("/foo/bar".into(), S2);
|
||||
|
@ -563,7 +533,7 @@ fn pkg_import_canonicalized_against_current_pkg() {
|
|||
#[rustfmt::skip]
|
||||
let toks = vec![
|
||||
PkgStart(S1, pkg_name),
|
||||
RefIdent(pkg_rel),
|
||||
PkgImport(pkg_rel),
|
||||
PkgEnd(S3),
|
||||
];
|
||||
|
||||
|
@ -599,7 +569,7 @@ fn pkg_doc() {
|
|||
|
||||
// Some object to place in-between the two
|
||||
// documentation blocks.
|
||||
RefIdent(id_import),
|
||||
PkgImport(id_import),
|
||||
|
||||
DocText(doc_b),
|
||||
];
|
||||
|
|
|
@ -87,6 +87,9 @@ pub enum AsgError {
|
|||
/// Attempted to open an expression in an invalid context.
|
||||
PkgExpected(Span),
|
||||
|
||||
/// Requested package import in a non-package context.
|
||||
InvalidPkgImport(SPair),
|
||||
|
||||
/// An expresion is not reachable by any other expression or
|
||||
/// identifier.
|
||||
///
|
||||
|
@ -184,6 +187,11 @@ impl Display for AsgError {
|
|||
write!(f, "invalid context for package close",)
|
||||
}
|
||||
PkgExpected(_) => write!(f, "expected package definition"),
|
||||
InvalidPkgImport(namespec) => write!(
|
||||
f,
|
||||
"unexpected package import {}",
|
||||
TtQuote::wrap(namespec)
|
||||
),
|
||||
DanglingExpr(_) => write!(
|
||||
f,
|
||||
"dangling expression (anonymous expression has no parent)"
|
||||
|
@ -300,6 +308,15 @@ impl Diagnostic for AsgError {
|
|||
vec![span.error("a package definition was expected here")]
|
||||
}
|
||||
|
||||
InvalidPkgImport(namespec) => {
|
||||
vec![
|
||||
namespec.error("this package cannot be imported here"),
|
||||
namespec.help(
|
||||
"imports must appear in the context of a package",
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
DanglingExpr(span) => vec![
|
||||
span.error(
|
||||
"this expression is unreachable and its value \
|
||||
|
|
|
@ -145,6 +145,9 @@ pub enum Nir {
|
|||
/// metavariable definition.
|
||||
Text(SPair),
|
||||
|
||||
/// Import a package identified by the provided namespec.
|
||||
Import(SPair),
|
||||
|
||||
/// "No-op" (no operation) that does nothing.
|
||||
///
|
||||
/// Since this is taking user input and effectively discarding it,
|
||||
|
@ -178,7 +181,7 @@ impl Nir {
|
|||
Open(_, _) | Close(_, _) => None,
|
||||
|
||||
BindIdent(spair) | RefSubject(spair) | Ref(spair) | Desc(spair)
|
||||
| Text(spair) => Some(spair.symbol()),
|
||||
| Text(spair) | Import(spair) => Some(spair.symbol()),
|
||||
|
||||
Noop(_) => None,
|
||||
}
|
||||
|
@ -214,6 +217,7 @@ impl Functor<SymbolId> for Nir {
|
|||
Ref(spair) => Ref(spair.map(f)),
|
||||
Desc(spair) => Desc(spair.map(f)),
|
||||
Text(spair) => Text(spair.map(f)),
|
||||
Import(spair) => Import(spair.map(f)),
|
||||
|
||||
Noop(_) => self,
|
||||
}
|
||||
|
@ -336,7 +340,7 @@ impl Token for Nir {
|
|||
Close(_, span) => *span,
|
||||
|
||||
BindIdent(spair) | RefSubject(spair) | Ref(spair) | Desc(spair)
|
||||
| Text(spair) => spair.span(),
|
||||
| Text(spair) | Import(spair) => spair.span(),
|
||||
|
||||
// A no-op is discarding user input,
|
||||
// so we still want to know where that is so that we can
|
||||
|
@ -374,6 +378,12 @@ impl Display for Nir {
|
|||
// output.
|
||||
Text(_) => write!(f, "text"),
|
||||
|
||||
Import(namespec) => write!(
|
||||
f,
|
||||
"import package with namespec {}",
|
||||
TtQuote::wrap(namespec)
|
||||
),
|
||||
|
||||
Noop(_) => write!(f, "no-op"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -262,6 +262,16 @@ impl ParseState for NirToAir {
|
|||
Transition(Ready).ok(Air::DocIndepClause(clause))
|
||||
}
|
||||
|
||||
(Ready, Import(namespec)) => {
|
||||
Transition(Ready).ok(Air::PkgImport(namespec))
|
||||
}
|
||||
|
||||
// Shouldn't happen in practice because nir::parse will not
|
||||
// produce this.
|
||||
// This assumption is only valid so long as that's the only
|
||||
// thing producing NIR.
|
||||
(st @ Meta(..), tok @ Import(_)) => Transition(st).dead(tok),
|
||||
|
||||
(_, tok @ (Todo(..) | TodoAttr(..))) => {
|
||||
crate::diagnostic_todo!(
|
||||
vec![tok.internal_error(
|
||||
|
|
|
@ -261,7 +261,7 @@ ele_parse! {
|
|||
/// for composing larger systems out of smaller components.
|
||||
ImportStmt := QN_IMPORT(_, ospan) {
|
||||
@ {
|
||||
QN_PACKAGE => Ref,
|
||||
QN_PACKAGE => Import,
|
||||
QN_EXPORT => TodoAttr,
|
||||
} => Noop(ospan.into()),
|
||||
// ^ we only care about the `Ref`
|
||||
|
|
Loading…
Reference in New Issue