tamer: obj::xmlo: Add Pkg nodes for identifiers
This modifies the xmlo reader, xmlo->AIR lowering, and AIR->ASG to introduce a package for identifiers. It does not yet, however, add edges from the package to the identifier. Once edges are added, the DFS will change in undesirable ways, which will require a new implementation. This is desirable to decouple from Petgraph anyway, and then will be able to restore the prior single-pass sort+cycle check. That will also encapsulate visiting behavior within the `asg::graph` module and, in turn, allow encapsulating `Asg.graph` finally. DEV-13162main
parent
34dad122fd
commit
48d9bca3b7
|
@ -232,27 +232,27 @@ impl ParseState for AirAggregate {
|
|||
tok @ (AirExpr(..) | AirBind(..) | AirTpl(..) | AirDoc(..)),
|
||||
) => Transition(Empty).err(AsgError::PkgExpected(tok.span())),
|
||||
|
||||
(Empty, AirIdent(IdentDecl(name, kind, src))) => {
|
||||
(Toplevel(oi_pkg), AirIdent(IdentDecl(name, kind, src))) => {
|
||||
let asg = ctx.asg_mut();
|
||||
let oi_root = asg.root(name);
|
||||
|
||||
asg.lookup_or_missing(oi_root, name)
|
||||
.declare(asg, name, kind, src)
|
||||
.map(|_| ())
|
||||
.transition(Empty)
|
||||
.transition(Toplevel(oi_pkg))
|
||||
}
|
||||
|
||||
(Empty, AirIdent(IdentExternDecl(name, kind, src))) => {
|
||||
(Toplevel(oi_pkg), AirIdent(IdentExternDecl(name, kind, src))) => {
|
||||
let asg = ctx.asg_mut();
|
||||
let oi_root = asg.root(name);
|
||||
|
||||
asg.lookup_or_missing(oi_root, name)
|
||||
.declare_extern(asg, name, kind, src)
|
||||
.map(|_| ())
|
||||
.transition(Empty)
|
||||
.transition(Toplevel(oi_pkg))
|
||||
}
|
||||
|
||||
(Empty, AirIdent(IdentDep(name, dep))) => {
|
||||
(Toplevel(oi_pkg), AirIdent(IdentDep(name, dep))) => {
|
||||
let asg = ctx.asg_mut();
|
||||
let oi_root = asg.root(dep);
|
||||
|
||||
|
@ -260,24 +260,24 @@ impl ParseState for AirAggregate {
|
|||
let oi_to = asg.lookup_or_missing(oi_root, dep);
|
||||
oi_from.add_opaque_dep(ctx.asg_mut(), oi_to);
|
||||
|
||||
Transition(Empty).incomplete()
|
||||
Transition(Toplevel(oi_pkg)).incomplete()
|
||||
}
|
||||
|
||||
(Empty, AirIdent(IdentFragment(name, text))) => {
|
||||
(Toplevel(oi_pkg), AirIdent(IdentFragment(name, text))) => {
|
||||
let asg = ctx.asg_mut();
|
||||
let oi_root = asg.root(name);
|
||||
|
||||
asg.lookup_or_missing(oi_root, name)
|
||||
.set_fragment(asg, text)
|
||||
.map(|_| ())
|
||||
.transition(Empty)
|
||||
.transition(Toplevel(oi_pkg))
|
||||
}
|
||||
|
||||
(Empty, AirIdent(IdentRoot(name))) => {
|
||||
(Toplevel(oi_pkg), AirIdent(IdentRoot(name))) => {
|
||||
let asg = ctx.asg_mut();
|
||||
asg.root(name).root_ident(asg, name);
|
||||
|
||||
Transition(Empty).incomplete()
|
||||
Transition(Toplevel(oi_pkg)).incomplete()
|
||||
}
|
||||
|
||||
(st, tok @ AirIdent(..)) => todo!("{st:?}, {tok:?}"),
|
||||
|
|
|
@ -24,12 +24,11 @@ use super::{super::Ident, *};
|
|||
use crate::{
|
||||
asg::{
|
||||
graph::object::{ObjectRel, ObjectRelFrom, ObjectRelatable},
|
||||
IdentKind, ObjectIndexRelTo, Source,
|
||||
IdentKind, ObjectIndexRelTo, Source, TransitionError,
|
||||
},
|
||||
parse::{ParseError, Parsed, Parser},
|
||||
span::dummy::*,
|
||||
};
|
||||
use std::assert_matches::assert_matches;
|
||||
|
||||
type Sut = AirAggregate;
|
||||
|
||||
|
@ -38,17 +37,38 @@ use Parsed::Incomplete;
|
|||
|
||||
#[test]
|
||||
fn ident_decl() {
|
||||
let id = SPair("foo".into(), S1);
|
||||
let id = SPair("foo".into(), S2);
|
||||
let kind = IdentKind::Tpl;
|
||||
let src = Source {
|
||||
src: Some("test/decl".into()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let toks = vec![IdentDecl(id, kind.clone(), src.clone())].into_iter();
|
||||
#[rustfmt::skip]
|
||||
let toks = vec![
|
||||
PkgStart(S1),
|
||||
IdentDecl(id, kind.clone(), src.clone()),
|
||||
// Attempt re-declaration.
|
||||
IdentDecl(id, kind.clone(), src.clone()),
|
||||
PkgEnd(S3),
|
||||
].into_iter();
|
||||
|
||||
let mut sut = Sut::parse(toks);
|
||||
|
||||
assert_eq!(Some(Ok(Incomplete)), sut.next());
|
||||
assert_eq!(
|
||||
#[rustfmt::skip]
|
||||
vec![
|
||||
Ok(Incomplete), // PkgStart
|
||||
Ok(Incomplete), // IdentDecl
|
||||
// Redeclare identifier
|
||||
Err(ParseError::StateError(AsgError::IdentTransition(
|
||||
TransitionError::Redeclare(id, S2)
|
||||
))),
|
||||
// RECOVERY: Ignore redeclaration
|
||||
Ok(Incomplete), // PkgEnd
|
||||
],
|
||||
sut.by_ref().collect::<Vec<Result<Parsed<()>, _>>>(),
|
||||
);
|
||||
|
||||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
|
@ -59,35 +79,51 @@ fn ident_decl() {
|
|||
assert_eq!(
|
||||
Ok(ident),
|
||||
Ident::declare(id)
|
||||
.resolve(S1, kind.clone(), src.clone())
|
||||
.resolve(S2, kind.clone(), src.clone())
|
||||
.as_ref(),
|
||||
);
|
||||
|
||||
// Re-instantiate the parser and test an error by attempting to
|
||||
// redeclare the same identifier.
|
||||
let bad_toks =
|
||||
vec![IdentDecl(SPair(id.symbol(), S2), kind, src)].into_iter();
|
||||
let mut sut = Sut::parse_with_context(bad_toks, asg);
|
||||
|
||||
assert_matches!(
|
||||
sut.next(),
|
||||
Some(Err(ParseError::StateError(AsgError::IdentTransition(_)))),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ident_extern_decl() {
|
||||
let id = SPair("foo".into(), S1);
|
||||
let id = SPair("foo".into(), S2);
|
||||
let re_id = SPair("foo".into(), S3);
|
||||
let kind = IdentKind::Tpl;
|
||||
let different_kind = IdentKind::Meta;
|
||||
let src = Source {
|
||||
src: Some("test/decl-extern".into()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let toks = vec![IdentExternDecl(id, kind.clone(), src.clone())].into_iter();
|
||||
#[rustfmt::skip]
|
||||
let toks = vec![
|
||||
PkgStart(S1),
|
||||
IdentExternDecl(id, kind.clone(), src.clone()),
|
||||
// Redeclare with a different kind
|
||||
IdentExternDecl(re_id, different_kind.clone(), src.clone()),
|
||||
PkgEnd(S4),
|
||||
].into_iter();
|
||||
|
||||
let mut sut = Sut::parse(toks);
|
||||
|
||||
assert_eq!(Some(Ok(Incomplete)), sut.next());
|
||||
assert_eq!(
|
||||
#[rustfmt::skip]
|
||||
vec![
|
||||
Ok(Incomplete), // PkgStart
|
||||
Ok(Incomplete), // IdentDecl
|
||||
// Redeclare identifier with a different kind
|
||||
Err(ParseError::StateError(AsgError::IdentTransition(
|
||||
TransitionError::ExternResolution(
|
||||
id,
|
||||
kind.clone(),
|
||||
(different_kind, S3)
|
||||
)
|
||||
))),
|
||||
// RECOVERY: Ignore redeclaration
|
||||
Ok(Incomplete), // PkgEnd
|
||||
],
|
||||
sut.by_ref().collect::<Vec<Result<Parsed<()>, _>>>(),
|
||||
);
|
||||
|
||||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
|
@ -97,32 +133,33 @@ fn ident_extern_decl() {
|
|||
|
||||
assert_eq!(
|
||||
Ok(ident),
|
||||
Ident::declare(id).extern_(S1, kind, src.clone()).as_ref(),
|
||||
);
|
||||
|
||||
// Re-instantiate the parser and test an error by attempting to
|
||||
// redeclare with a different kind.
|
||||
let different_kind = IdentKind::Meta;
|
||||
let bad_toks =
|
||||
vec![IdentExternDecl(SPair(id.symbol(), S2), different_kind, src)]
|
||||
.into_iter();
|
||||
let mut sut = Sut::parse_with_context(bad_toks, asg);
|
||||
|
||||
assert_matches!(
|
||||
sut.next(),
|
||||
Some(Err(ParseError::StateError(AsgError::IdentTransition(_)))),
|
||||
Ident::declare(id).extern_(S2, kind, src.clone()).as_ref(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ident_dep() {
|
||||
let id = SPair("foo".into(), S1);
|
||||
let dep = SPair("dep".into(), S2);
|
||||
let id = SPair("foo".into(), S2);
|
||||
let dep = SPair("dep".into(), S3);
|
||||
|
||||
#[rustfmt::skip]
|
||||
let toks = vec![
|
||||
PkgStart(S1),
|
||||
IdentDep(id, dep),
|
||||
PkgEnd(S4),
|
||||
].into_iter();
|
||||
|
||||
let toks = vec![IdentDep(id, dep)].into_iter();
|
||||
let mut sut = Sut::parse(toks);
|
||||
|
||||
assert_eq!(Some(Ok(Incomplete)), sut.next());
|
||||
assert_eq!(
|
||||
#[rustfmt::skip]
|
||||
Ok(vec![
|
||||
Incomplete, // PkgStart
|
||||
Incomplete, // IdentDep
|
||||
Incomplete, // PkgEnd
|
||||
]),
|
||||
sut.by_ref().collect(),
|
||||
);
|
||||
|
||||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
|
@ -135,7 +172,7 @@ fn ident_dep() {
|
|||
|
||||
#[test]
|
||||
fn ident_fragment() {
|
||||
let id = SPair("frag".into(), S1);
|
||||
let id = SPair("frag".into(), S2);
|
||||
let kind = IdentKind::Tpl;
|
||||
let src = Source {
|
||||
src: Some("test/frag".into()),
|
||||
|
@ -143,18 +180,36 @@ fn ident_fragment() {
|
|||
};
|
||||
let frag = "fragment text".into();
|
||||
|
||||
#[rustfmt::skip]
|
||||
let toks = vec![
|
||||
// Identifier must be declared before it can be given a
|
||||
// fragment.
|
||||
IdentDecl(id, kind.clone(), src.clone()),
|
||||
IdentFragment(id, frag),
|
||||
]
|
||||
.into_iter();
|
||||
PkgStart(S1),
|
||||
// Identifier must be declared before it can be given a
|
||||
// fragment.
|
||||
IdentDecl(id, kind.clone(), src.clone()),
|
||||
IdentFragment(id, frag),
|
||||
// Reset fragment (error)
|
||||
IdentFragment(id, frag),
|
||||
// RECOVERY: Ignore reset
|
||||
PkgEnd(S4),
|
||||
] .into_iter();
|
||||
|
||||
let mut sut = Sut::parse(toks);
|
||||
|
||||
assert_eq!(Some(Ok(Incomplete)), sut.next()); // IdentDecl
|
||||
assert_eq!(Some(Ok(Incomplete)), sut.next()); // IdentFragment
|
||||
assert_eq!(
|
||||
#[rustfmt::skip]
|
||||
vec![
|
||||
Ok(Incomplete), // PkgStart
|
||||
Ok(Incomplete), // IdentDecl
|
||||
Ok(Incomplete), // IdentFragment
|
||||
// Reset fragment
|
||||
Err(ParseError::StateError(AsgError::IdentTransition(
|
||||
TransitionError::BadFragmentDest(id)
|
||||
))),
|
||||
// RECOVERY: Ignore reset
|
||||
Ok(Incomplete), // PkgEnd
|
||||
],
|
||||
sut.by_ref().collect::<Vec<Result<Parsed<()>, _>>>(),
|
||||
);
|
||||
|
||||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
|
@ -165,32 +220,36 @@ fn ident_fragment() {
|
|||
assert_eq!(
|
||||
Ok(ident),
|
||||
Ident::declare(id)
|
||||
.resolve(S1, kind.clone(), src.clone())
|
||||
.resolve(S2, kind.clone(), src.clone())
|
||||
.and_then(|resolved| resolved.set_fragment(frag))
|
||||
.as_ref(),
|
||||
);
|
||||
|
||||
// Re-instantiate the parser and test an error by attempting to
|
||||
// re-set the fragment.
|
||||
let bad_toks = vec![IdentFragment(id, frag)].into_iter();
|
||||
let mut sut = Sut::parse_with_context(bad_toks, asg);
|
||||
|
||||
assert_matches!(
|
||||
sut.next(),
|
||||
Some(Err(ParseError::StateError(AsgError::IdentTransition(_)))),
|
||||
);
|
||||
}
|
||||
|
||||
// Adding a root before the identifier exists should add a
|
||||
// `Ident::Missing`.
|
||||
#[test]
|
||||
fn ident_root_missing() {
|
||||
let id = SPair("toroot".into(), S1);
|
||||
let id = SPair("toroot".into(), S2);
|
||||
|
||||
#[rustfmt::skip]
|
||||
let toks = vec![
|
||||
PkgStart(S1),
|
||||
IdentRoot(id),
|
||||
PkgEnd(S3),
|
||||
].into_iter();
|
||||
|
||||
let toks = vec![IdentRoot(id)].into_iter();
|
||||
let mut sut = Sut::parse(toks);
|
||||
|
||||
assert_eq!(Some(Ok(Incomplete)), sut.next());
|
||||
assert_eq!(
|
||||
#[rustfmt::skip]
|
||||
Ok(vec![
|
||||
Incomplete, // PkgStart
|
||||
Incomplete, // IdentRoot
|
||||
Incomplete, // PkgEnd
|
||||
]),
|
||||
sut.by_ref().collect(),
|
||||
);
|
||||
|
||||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
|
@ -208,7 +267,7 @@ fn ident_root_missing() {
|
|||
|
||||
#[test]
|
||||
fn ident_root_existing() {
|
||||
let id = SPair("toroot".into(), S1);
|
||||
let id = SPair("toroot".into(), S2);
|
||||
let kind = IdentKind::Tpl;
|
||||
let src = Source {
|
||||
src: Some("test/root-existing".into()),
|
||||
|
@ -219,16 +278,27 @@ fn ident_root_existing() {
|
|||
// otherwise we won't be testing the right thing.
|
||||
assert!(!kind.is_auto_root());
|
||||
|
||||
#[rustfmt::skip]
|
||||
let toks = vec![
|
||||
IdentDecl(id, kind.clone(), src.clone()),
|
||||
IdentRoot(SPair(id.symbol(), S2)),
|
||||
PkgStart(S1),
|
||||
IdentDecl(id, kind.clone(), src.clone()),
|
||||
IdentRoot(SPair(id.symbol(), S3)),
|
||||
PkgEnd(S3),
|
||||
]
|
||||
.into_iter();
|
||||
|
||||
let mut sut = Sut::parse(toks);
|
||||
|
||||
assert_eq!(Some(Ok(Incomplete)), sut.next()); // IdentDecl
|
||||
assert_eq!(Some(Ok(Incomplete)), sut.next()); // IdentRoot
|
||||
assert_eq!(
|
||||
#[rustfmt::skip]
|
||||
Ok(vec![
|
||||
Incomplete, // PkgStart
|
||||
Incomplete, // IdentDecl
|
||||
Incomplete, // IdentRoot
|
||||
Incomplete, // PkgEnd
|
||||
]),
|
||||
sut.by_ref().collect(),
|
||||
);
|
||||
|
||||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
|
@ -240,7 +310,7 @@ fn ident_root_existing() {
|
|||
assert_eq!(
|
||||
Ok(ident),
|
||||
Ident::declare(id)
|
||||
.resolve(S1, kind.clone(), src.clone())
|
||||
.resolve(S2, kind.clone(), src.clone())
|
||||
.as_ref()
|
||||
);
|
||||
|
||||
|
@ -257,21 +327,36 @@ fn declare_kind_auto_root() {
|
|||
assert!(auto_kind.is_auto_root());
|
||||
assert!(!no_auto_kind.is_auto_root());
|
||||
|
||||
let id_auto = SPair("auto_root".into(), S1);
|
||||
let id_no_auto = SPair("no_auto_root".into(), S2);
|
||||
let id_auto = SPair("auto_root".into(), S2);
|
||||
let id_no_auto = SPair("no_auto_root".into(), S3);
|
||||
|
||||
let src = Source {
|
||||
src: Some("src/pkg".into()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
#[rustfmt::skip]
|
||||
let toks = [
|
||||
// auto-rooting
|
||||
IdentDecl(id_auto, auto_kind, Default::default()),
|
||||
// non-auto-rooting
|
||||
IdentDecl(id_no_auto, no_auto_kind, Default::default()),
|
||||
]
|
||||
.into_iter();
|
||||
PkgStart(S1),
|
||||
// auto-rooting
|
||||
IdentDecl(id_auto, auto_kind, src.clone()),
|
||||
// non-auto-rooting
|
||||
IdentDecl(id_no_auto, no_auto_kind, src),
|
||||
PkgEnd(S4),
|
||||
].into_iter();
|
||||
|
||||
let mut sut = Sut::parse(toks);
|
||||
|
||||
assert_eq!(Some(Ok(Incomplete)), sut.next());
|
||||
assert_eq!(Some(Ok(Incomplete)), sut.next());
|
||||
assert_eq!(
|
||||
#[rustfmt::skip]
|
||||
Ok(vec![
|
||||
Incomplete, // PkgStart
|
||||
Incomplete, // IdentDecl
|
||||
Incomplete, // IdentDecl
|
||||
Incomplete, // PkgEnd
|
||||
]),
|
||||
sut.by_ref().collect(),
|
||||
);
|
||||
|
||||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
|
|
|
@ -257,12 +257,9 @@ impl Asg {
|
|||
/// you must resolve the [`ObjectIndex`] and inspect it.
|
||||
///
|
||||
/// See [`Self::index`] for more information.
|
||||
pub(super) fn lookup_or_missing<
|
||||
O: ObjectRelatable,
|
||||
OS: ObjectIndexTreeRelTo<O>,
|
||||
>(
|
||||
pub(super) fn lookup_or_missing<O: ObjectRelatable>(
|
||||
&mut self,
|
||||
imm_env: OS,
|
||||
imm_env: impl ObjectIndexTreeRelTo<O>,
|
||||
name: SPair,
|
||||
) -> ObjectIndex<O>
|
||||
where
|
||||
|
|
|
@ -72,6 +72,10 @@ where
|
|||
Object::Root(_) => (),
|
||||
Object::Ident(ident) => dest.push(ident)?,
|
||||
|
||||
// Identifiers are parented to their packages,
|
||||
// but they're nothing more than containers.
|
||||
Object::Pkg(_) => (),
|
||||
|
||||
obj => {
|
||||
diagnostic_unreachable!(
|
||||
obj.internal_error(
|
||||
|
|
|
@ -92,6 +92,7 @@ type PackageSPair = SPair;
|
|||
pub enum XmloToAir {
|
||||
#[default]
|
||||
PackageExpected,
|
||||
PackageFound(Span),
|
||||
Package(PackageSPair),
|
||||
SymDep(PackageSPair, SPair),
|
||||
/// End of header (EOH) reached.
|
||||
|
@ -113,7 +114,13 @@ impl ParseState for XmloToAir {
|
|||
use XmloToAir::*;
|
||||
|
||||
match (self, tok) {
|
||||
(PackageExpected, PkgName(name)) => {
|
||||
(PackageExpected, PkgStart(span)) => {
|
||||
Transition(PackageFound(span)).ok(Air::PkgStart(span))
|
||||
}
|
||||
|
||||
(PackageExpected, tok) => Transition(PackageExpected).dead(tok),
|
||||
|
||||
(PackageFound(_), PkgName(name)) => {
|
||||
if ctx.is_first() {
|
||||
ctx.prog_name = Some(name.symbol());
|
||||
}
|
||||
|
@ -143,7 +150,7 @@ impl ParseState for XmloToAir {
|
|||
Transition(Package(name)).ok(Air::IdentRoot(pkg_elig))
|
||||
}
|
||||
|
||||
(st @ (PackageExpected | Package(..)), PkgProgramFlag(_)) => {
|
||||
(st @ (PackageFound(..) | Package(..)), PkgProgramFlag(_)) => {
|
||||
// TODO: Unused
|
||||
Transition(st).incomplete()
|
||||
}
|
||||
|
@ -219,13 +226,15 @@ impl ParseState for XmloToAir {
|
|||
// Note that this uses `incomplete` because we have nothing
|
||||
// to yield,
|
||||
// but we are in fact done.
|
||||
Transition(Done(span)).incomplete()
|
||||
Transition(Done(span)).ok(Air::PkgEnd(span))
|
||||
}
|
||||
|
||||
(st @ Package(..), tok @ (PkgName(..) | Symbol(..))) => {
|
||||
Transition(st).dead(tok)
|
||||
}
|
||||
(st @ (PackageExpected | SymDep(..) | Done(..)), tok) => {
|
||||
(
|
||||
st @ Package(..),
|
||||
tok @ (PkgStart(..) | PkgName(..) | Symbol(..)),
|
||||
) => Transition(st).dead(tok),
|
||||
|
||||
(st @ (PackageFound(..) | SymDep(..) | Done(..)), tok) => {
|
||||
Transition(st).dead(tok)
|
||||
}
|
||||
}
|
||||
|
@ -242,6 +251,7 @@ impl Display for XmloToAir {
|
|||
|
||||
match self {
|
||||
PackageExpected => write!(f, "expecting package definition"),
|
||||
PackageFound(_) => write!(f, "package found, awaiting definition"),
|
||||
Package(name) => {
|
||||
write!(f, "expecting package `/{name}` declarations")
|
||||
}
|
||||
|
|
|
@ -37,9 +37,10 @@ fn data_from_package_event() {
|
|||
let relroot = "some/path".into();
|
||||
|
||||
let toks = vec![
|
||||
PkgName(SPair(name, S1)),
|
||||
PkgRootPath(SPair(relroot, S2)),
|
||||
Eoh(S3),
|
||||
PkgStart(S1),
|
||||
PkgName(SPair(name, S2)),
|
||||
PkgRootPath(SPair(relroot, S4)),
|
||||
Eoh(S4),
|
||||
]
|
||||
.into_iter();
|
||||
|
||||
|
@ -47,9 +48,10 @@ fn data_from_package_event() {
|
|||
|
||||
assert_eq!(
|
||||
Ok(vec![
|
||||
Incomplete, // PkgName
|
||||
Incomplete, // PkgRootPath
|
||||
Incomplete, // Eoh
|
||||
O(Air::PkgStart(S1)),
|
||||
Incomplete, // PkgName
|
||||
Incomplete, // PkgRootPath
|
||||
O(Air::PkgEnd(S4)), // Eoh
|
||||
]),
|
||||
sut.by_ref().collect(),
|
||||
);
|
||||
|
@ -66,16 +68,18 @@ fn adds_elig_as_root() {
|
|||
let elig_sym = "elig".into();
|
||||
|
||||
let toks = vec![
|
||||
PkgName(SPair(name, S1)),
|
||||
PkgEligClassYields(SPair(elig_sym, S2)),
|
||||
Eoh(S3),
|
||||
PkgStart(S1),
|
||||
PkgName(SPair(name, S2)),
|
||||
PkgEligClassYields(SPair(elig_sym, S3)),
|
||||
Eoh(S4),
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
Ok(vec![
|
||||
O(Air::PkgStart(S1)),
|
||||
Incomplete, // PkgName
|
||||
O(Air::IdentRoot(SPair(elig_sym, S2))),
|
||||
Incomplete, // Eoh
|
||||
O(Air::IdentRoot(SPair(elig_sym, S3))),
|
||||
O(Air::PkgEnd(S4)), // Eoh
|
||||
]),
|
||||
Sut::parse(toks.into_iter()).collect(),
|
||||
);
|
||||
|
@ -88,20 +92,22 @@ fn adds_sym_deps() {
|
|||
let sym_to2 = "to2".into();
|
||||
|
||||
let toks = vec![
|
||||
PkgName(SPair("name".into(), S1)),
|
||||
SymDepStart(SPair(sym_from, S2)),
|
||||
Symbol(SPair(sym_to1, S3)),
|
||||
Symbol(SPair(sym_to2, S4)),
|
||||
Eoh(S1),
|
||||
PkgStart(S1),
|
||||
PkgName(SPair("name".into(), S2)),
|
||||
SymDepStart(SPair(sym_from, S3)),
|
||||
Symbol(SPair(sym_to1, S4)),
|
||||
Symbol(SPair(sym_to2, S5)),
|
||||
Eoh(S6),
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
Ok(vec![
|
||||
O(Air::PkgStart(S1)),
|
||||
Incomplete, // PkgName
|
||||
Incomplete, // SymDepStart
|
||||
O(Air::IdentDep(SPair(sym_from, S2), SPair(sym_to1, S3))),
|
||||
O(Air::IdentDep(SPair(sym_from, S2), SPair(sym_to2, S4))),
|
||||
Incomplete, // Eoh
|
||||
O(Air::IdentDep(SPair(sym_from, S3), SPair(sym_to1, S4))),
|
||||
O(Air::IdentDep(SPair(sym_from, S3), SPair(sym_to2, S5))),
|
||||
O(Air::PkgEnd(S6)),
|
||||
]),
|
||||
Sut::parse(toks.into_iter()).collect(),
|
||||
);
|
||||
|
@ -114,32 +120,34 @@ fn sym_decl_with_src_not_added_and_populates_found() {
|
|||
let src_b = "src_b".into();
|
||||
|
||||
let toks = vec![
|
||||
PkgName(SPair("name".into(), S1)),
|
||||
PkgStart(S1),
|
||||
PkgName(SPair("name".into(), S2)),
|
||||
SymDecl(
|
||||
SPair(sym, S2),
|
||||
SPair(sym, S3),
|
||||
SymAttrs {
|
||||
src: Some(src_a),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
SymDecl(
|
||||
SPair(sym, S3),
|
||||
SPair(sym, S4),
|
||||
SymAttrs {
|
||||
src: Some(src_b),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
Eoh(S1),
|
||||
Eoh(S5),
|
||||
];
|
||||
|
||||
let mut sut = Sut::parse(toks.into_iter());
|
||||
|
||||
assert_eq!(
|
||||
Ok(vec![
|
||||
O(Air::PkgStart(S1)),
|
||||
Incomplete, // PkgName
|
||||
Incomplete, // SymDecl (@src)
|
||||
Incomplete, // SymDecl (@src)
|
||||
Incomplete, // Eoh
|
||||
O(Air::PkgEnd(S5)),
|
||||
]),
|
||||
sut.by_ref().collect(),
|
||||
);
|
||||
|
@ -163,9 +171,10 @@ fn sym_decl_added_to_graph() {
|
|||
let pkg_name = "pkg name".into();
|
||||
|
||||
let toks = vec![
|
||||
PkgName(SPair("name".into(), S1)),
|
||||
PkgStart(S1),
|
||||
PkgName(SPair("name".into(), S2)),
|
||||
SymDecl(
|
||||
SPair(sym_extern, S1),
|
||||
SPair(sym_extern, S3),
|
||||
SymAttrs {
|
||||
pkg_name: Some(pkg_name),
|
||||
extern_: true,
|
||||
|
@ -174,7 +183,7 @@ fn sym_decl_added_to_graph() {
|
|||
},
|
||||
),
|
||||
SymDecl(
|
||||
SPair(sym_non_extern, S2),
|
||||
SPair(sym_non_extern, S4),
|
||||
SymAttrs {
|
||||
pkg_name: Some(pkg_name),
|
||||
ty: Some(SymType::Meta),
|
||||
|
@ -182,7 +191,7 @@ fn sym_decl_added_to_graph() {
|
|||
},
|
||||
),
|
||||
SymDecl(
|
||||
SPair(sym_map, S3),
|
||||
SPair(sym_map, S5),
|
||||
SymAttrs {
|
||||
pkg_name: Some(pkg_name),
|
||||
ty: Some(SymType::Map),
|
||||
|
@ -190,24 +199,25 @@ fn sym_decl_added_to_graph() {
|
|||
},
|
||||
),
|
||||
SymDecl(
|
||||
SPair(sym_retmap, S4),
|
||||
SPair(sym_retmap, S6),
|
||||
SymAttrs {
|
||||
pkg_name: Some(pkg_name),
|
||||
ty: Some(SymType::RetMap),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
Eoh(S1),
|
||||
Eoh(S7),
|
||||
];
|
||||
|
||||
let mut sut = Sut::parse(toks.into_iter());
|
||||
|
||||
// Note that each of these will have their package names cleared
|
||||
// since this is considered to be the first package encountered.
|
||||
assert_eq!(Some(Ok(O(Air::PkgStart(S1)))), sut.next()); // PkgStart
|
||||
assert_eq!(Some(Ok(Incomplete)), sut.next()); // PkgName
|
||||
assert_eq!(
|
||||
Some(Ok(O(Air::IdentExternDecl(
|
||||
SPair(sym_extern, S1),
|
||||
SPair(sym_extern, S3),
|
||||
IdentKind::Meta,
|
||||
Source {
|
||||
pkg_name: None,
|
||||
|
@ -218,7 +228,7 @@ fn sym_decl_added_to_graph() {
|
|||
);
|
||||
assert_eq!(
|
||||
Some(Ok(O(Air::IdentDecl(
|
||||
SPair(sym_non_extern, S2),
|
||||
SPair(sym_non_extern, S4),
|
||||
IdentKind::Meta,
|
||||
Source {
|
||||
pkg_name: None,
|
||||
|
@ -229,7 +239,7 @@ fn sym_decl_added_to_graph() {
|
|||
);
|
||||
assert_eq!(
|
||||
Some(Ok(O(Air::IdentDecl(
|
||||
SPair(sym_map, S3),
|
||||
SPair(sym_map, S5),
|
||||
IdentKind::Map,
|
||||
Source {
|
||||
pkg_name: None,
|
||||
|
@ -240,7 +250,7 @@ fn sym_decl_added_to_graph() {
|
|||
);
|
||||
assert_eq!(
|
||||
Some(Ok(O(Air::IdentDecl(
|
||||
SPair(sym_retmap, S4),
|
||||
SPair(sym_retmap, S6),
|
||||
IdentKind::RetMap,
|
||||
Source {
|
||||
pkg_name: None,
|
||||
|
@ -249,7 +259,8 @@ fn sym_decl_added_to_graph() {
|
|||
)))),
|
||||
sut.next(),
|
||||
);
|
||||
assert_eq!(Some(Ok(Incomplete)), sut.next()); // Eoh
|
||||
|
||||
assert_eq!(Some(Ok(O(Air::PkgEnd(S7)))), sut.next());
|
||||
|
||||
let ctx = sut.finalize().unwrap().into_context();
|
||||
|
||||
|
@ -274,30 +285,32 @@ fn sym_decl_pkg_name_retained_if_not_first() {
|
|||
};
|
||||
|
||||
let toks = vec![
|
||||
PkgName(SPair(pkg_name, S1)),
|
||||
PkgStart(S1),
|
||||
PkgName(SPair(pkg_name, S2)),
|
||||
SymDecl(
|
||||
SPair(sym, S2),
|
||||
SPair(sym, S3),
|
||||
SymAttrs {
|
||||
pkg_name: Some(pkg_name),
|
||||
ty: Some(SymType::Meta),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
Eoh(S1),
|
||||
Eoh(S4),
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
Ok(vec![
|
||||
O(Air::PkgStart(S1)),
|
||||
Incomplete, // PkgName
|
||||
O(Air::IdentDecl(
|
||||
SPair(sym, S2),
|
||||
SPair(sym, S3),
|
||||
IdentKind::Meta,
|
||||
Source {
|
||||
pkg_name: Some(pkg_name),
|
||||
..Default::default()
|
||||
}
|
||||
)),
|
||||
Incomplete, // Eoh
|
||||
O(Air::PkgEnd(S4)),
|
||||
]),
|
||||
Sut::parse_with_context(toks.into_iter(), ctx).collect(),
|
||||
);
|
||||
|
@ -316,30 +329,32 @@ fn sym_decl_pkg_name_set_if_empty_and_not_first() {
|
|||
};
|
||||
|
||||
let toks = vec![
|
||||
PkgName(SPair(pkg_name, S1)),
|
||||
PkgStart(S1),
|
||||
PkgName(SPair(pkg_name, S2)),
|
||||
SymDecl(
|
||||
SPair(sym, S2),
|
||||
SPair(sym, S3),
|
||||
SymAttrs {
|
||||
// No name
|
||||
ty: Some(SymType::Meta),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
Eoh(S1),
|
||||
Eoh(S4),
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
Ok(vec![
|
||||
O(Air::PkgStart(S1)),
|
||||
Incomplete, // PkgName
|
||||
O(Air::IdentDecl(
|
||||
SPair(sym, S2),
|
||||
SPair(sym, S3),
|
||||
IdentKind::Meta,
|
||||
Source {
|
||||
pkg_name: Some(pkg_name), // Name inherited
|
||||
..Default::default()
|
||||
},
|
||||
)),
|
||||
Incomplete, // Eoh
|
||||
O(Air::PkgEnd(S4)),
|
||||
]),
|
||||
Sut::parse_with_context(toks.into_iter(), ctx).collect(),
|
||||
);
|
||||
|
@ -351,8 +366,9 @@ fn ident_kind_conversion_error_propagates() {
|
|||
let bad_attrs = SymAttrs::default();
|
||||
|
||||
let toks = vec![
|
||||
PkgName(SPair("name".into(), S1)),
|
||||
SymDecl(SPair(sym, S2), bad_attrs),
|
||||
PkgStart(S1),
|
||||
PkgName(SPair("name".into(), S2)),
|
||||
SymDecl(SPair(sym, S3), bad_attrs),
|
||||
Eoh(S1),
|
||||
];
|
||||
|
||||
|
@ -367,16 +383,18 @@ fn sets_fragment() {
|
|||
let frag = FragmentText::from("foo");
|
||||
|
||||
let toks = vec![
|
||||
PkgName(SPair("name".into(), S1)),
|
||||
Fragment(SPair(sym, S2), frag.clone()),
|
||||
Eoh(S1),
|
||||
PkgStart(S1),
|
||||
PkgName(SPair("name".into(), S2)),
|
||||
Fragment(SPair(sym, S3), frag.clone()),
|
||||
Eoh(S4),
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
Ok(vec![
|
||||
O(Air::PkgStart(S1)),
|
||||
Incomplete, // PkgName
|
||||
O(Air::IdentFragment(SPair(sym, S2), frag)),
|
||||
Incomplete, // Eoh
|
||||
O(Air::IdentFragment(SPair(sym, S3), frag)),
|
||||
O(Air::PkgEnd(S4)),
|
||||
]),
|
||||
Sut::parse(toks.into_iter()).collect(),
|
||||
);
|
||||
|
|
|
@ -49,6 +49,9 @@ use crate::{
|
|||
/// be useful and can't be easily skipped without parsing.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum XmloToken {
|
||||
/// A new package has been found.
|
||||
PkgStart(Span),
|
||||
|
||||
/// Canonical package name.
|
||||
PkgName(SPair),
|
||||
/// Relative path from package to project root.
|
||||
|
@ -109,7 +112,8 @@ impl Token for XmloToken {
|
|||
// important since these initial tokens seed
|
||||
// `Parser::last_span`,
|
||||
// which is used for early error messages.
|
||||
PkgName(SPair(_, span))
|
||||
PkgStart(span)
|
||||
| PkgName(SPair(_, span))
|
||||
| PkgRootPath(SPair(_, span))
|
||||
| PkgProgramFlag(span)
|
||||
| PkgEligClassYields(SPair(_, span))
|
||||
|
@ -127,6 +131,7 @@ impl Display for XmloToken {
|
|||
use XmloToken::*;
|
||||
|
||||
match self {
|
||||
PkgStart(_) => write!(f, "package start"),
|
||||
PkgName(sym) => write!(f, "package of name {}", TtQuote::wrap(sym)),
|
||||
PkgRootPath(sym) => {
|
||||
write!(f, "package root path {}", TtQuote::wrap(sym))
|
||||
|
@ -208,8 +213,9 @@ impl<SS: XmloState, SD: XmloState, SF: XmloState> ParseState
|
|||
use XmloReader::*;
|
||||
|
||||
match (self, tok) {
|
||||
(Ready, Xirf::Open(QN_LV_PACKAGE | QN_PACKAGE, span, ..)) => {
|
||||
Transition(Package(span.tag_span())).incomplete()
|
||||
(Ready, Xirf::Open(QN_LV_PACKAGE | QN_PACKAGE, ospan, ..)) => {
|
||||
let span = ospan.tag_span();
|
||||
Transition(Package(span)).ok(XmloToken::PkgStart(span))
|
||||
}
|
||||
|
||||
(Ready, tok) => {
|
||||
|
|
|
@ -81,7 +81,7 @@ fn common_parses_package_attrs(package: QName) {
|
|||
|
||||
assert_eq!(
|
||||
Ok(vec![
|
||||
Incomplete,
|
||||
O(PkgStart(S1)),
|
||||
O(PkgName(SPair(name, S3))),
|
||||
O(PkgRootPath(SPair(relroot, S3))),
|
||||
// Span for the program flag is the attr name,
|
||||
|
@ -127,7 +127,7 @@ fn ignores_unknown_package_attr() {
|
|||
|
||||
assert_eq!(
|
||||
Ok(vec![
|
||||
Incomplete,
|
||||
O(PkgStart(S1)),
|
||||
O(PkgName(SPair(name, S3))),
|
||||
Incomplete, // The unknown attribute
|
||||
Incomplete,
|
||||
|
@ -669,6 +669,7 @@ fn xmlo_composite_parsers_header() {
|
|||
|
||||
assert_eq!(
|
||||
Ok(vec![
|
||||
O(PkgStart(S1)),
|
||||
O(SymDecl(SPair(sym_name, S3), Default::default(),)),
|
||||
O(SymDepStart(SPair(symdep_name, S3))),
|
||||
O(Fragment(SPair(symfrag_id, S4), frag)),
|
||||
|
|
Loading…
Reference in New Issue