tamer: NIR->xmli: Represent package imports
This doesn't do the actual hard work yet of resolving and loading a package, but it does place it on the graph and re-derive it into the xmli output. DEV-13708main
parent
82e228009d
commit
f4653790da
|
@ -165,8 +165,11 @@ impl ParseState for AirAggregate {
|
|||
(st @ Toplevel(..), AirBind(BindIdent(id))) => {
|
||||
Transition(st).err(AsgError::InvalidBindContext(id))
|
||||
}
|
||||
(st @ Toplevel(..), AirBind(RefIdent(id))) => {
|
||||
Transition(st).err(AsgError::InvalidRefContext(id))
|
||||
|
||||
// Package import
|
||||
(Toplevel(oi_pkg), AirBind(RefIdent(pathspec))) => {
|
||||
oi_pkg.import(ctx.asg_mut(), pathspec);
|
||||
Transition(Toplevel(oi_pkg)).incomplete()
|
||||
}
|
||||
|
||||
// Note: We unfortunately can't match on `AirExpr | AirBind`
|
||||
|
|
|
@ -820,60 +820,6 @@ fn expr_ref_to_ident() {
|
|||
assert!(oi_ident_bar.is_bound_to(&asg, oi_expr_bar));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expr_ref_outside_of_expr_context() {
|
||||
let id_pre = SPair("pre".into(), S2);
|
||||
let id_foo = SPair("foo".into(), S4);
|
||||
|
||||
#[rustfmt::skip]
|
||||
let toks = vec![
|
||||
// We need to first bring ourselves out of the context of the
|
||||
// package header.
|
||||
Air::ExprStart(ExprOp::Sum, S1),
|
||||
Air::BindIdent(id_pre),
|
||||
Air::ExprEnd(S3),
|
||||
|
||||
// This will fail since we're not in an expression context.
|
||||
Air::RefIdent(id_foo),
|
||||
|
||||
// RECOVERY: Simply ignore the above.
|
||||
Air::ExprStart(ExprOp::Sum, S1),
|
||||
Air::BindIdent(id_foo),
|
||||
Air::ExprEnd(S3),
|
||||
];
|
||||
|
||||
let mut sut = parse_as_pkg_body(toks);
|
||||
|
||||
assert_eq!(
|
||||
#[rustfmt::skip]
|
||||
vec![
|
||||
Ok(Parsed::Incomplete), // PkgStart
|
||||
Ok(Parsed::Incomplete), // ExprStart
|
||||
Ok(Parsed::Incomplete), // BindIdent
|
||||
Ok(Parsed::Incomplete), // ExprEnd
|
||||
|
||||
// Now we're past the header and in expression parsing mode.
|
||||
Err(ParseError::StateError(AsgError::InvalidRefContext(
|
||||
id_foo
|
||||
))),
|
||||
|
||||
// RECOVERY: Proceed as normal
|
||||
Ok(Parsed::Incomplete), // ExprStart
|
||||
Ok(Parsed::Incomplete), // BindIdent
|
||||
Ok(Parsed::Incomplete), // ExprEnd
|
||||
Ok(Parsed::Incomplete), // PkgEnd
|
||||
],
|
||||
sut.by_ref().collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
// Verify that the identifier was bound just to have some confidence in
|
||||
// the recovery.
|
||||
let expr = asg.expect_ident_obj::<Expr>(id_foo);
|
||||
assert_eq!(expr.span(), S1.merge(S3).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn idents_share_defining_pkg() {
|
||||
let id_foo = SPair("foo".into(), S3);
|
||||
|
|
|
@ -311,6 +311,35 @@ fn nested_open_pkg() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pkg_import() {
|
||||
let pathspec = SPair("foo/bar".into(), S2);
|
||||
|
||||
#[rustfmt::skip]
|
||||
let toks = vec![
|
||||
Air::PkgStart(S1),
|
||||
Air::RefIdent(pathspec),
|
||||
Air::PkgEnd(S3),
|
||||
];
|
||||
|
||||
let mut sut = Sut::parse(toks.into_iter());
|
||||
assert!(sut.all(|x| x.is_ok()));
|
||||
|
||||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
let import = asg
|
||||
.root(S1)
|
||||
.edges_filtered::<Pkg>(&asg)
|
||||
.next()
|
||||
.expect("cannot find package from root")
|
||||
.edges_filtered::<Pkg>(&asg)
|
||||
.next()
|
||||
.expect("cannot find imported package")
|
||||
.resolve(&asg);
|
||||
|
||||
assert_eq!(pathspec, import.pathspec());
|
||||
}
|
||||
|
||||
/// Parse using [`Sut`] when the test does not care about the outer package.
|
||||
pub fn parse_as_pkg_body<I: IntoIterator<Item = Air>>(
|
||||
toks: I,
|
||||
|
|
|
@ -20,26 +20,52 @@
|
|||
//! Package object on the ASG.
|
||||
|
||||
use super::{
|
||||
Ident, Object, ObjectIndex, ObjectRel, ObjectRelFrom, ObjectRelTy,
|
||||
ObjectRelatable, ObjectTreeRelTo, Tpl,
|
||||
Ident, Object, ObjectIndex, ObjectIndexRelTo, ObjectRel, ObjectRelFrom,
|
||||
ObjectRelTy, ObjectRelatable, ObjectTreeRelTo, Tpl,
|
||||
};
|
||||
use crate::{
|
||||
asg::Asg,
|
||||
f::Functor,
|
||||
parse::{util::SPair, Token},
|
||||
span::Span,
|
||||
sym::st::raw::WS_EMPTY,
|
||||
};
|
||||
use crate::{asg::Asg, f::Functor, span::Span};
|
||||
use std::fmt::Display;
|
||||
|
||||
#[cfg(doc)]
|
||||
use super::ObjectKind;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Pkg(Span);
|
||||
pub struct Pkg(Span, PathSpec);
|
||||
|
||||
/// Package path specification used to import this package.
|
||||
///
|
||||
/// TODO: This is simply punting on handling of imports for now.
|
||||
type PathSpec = SPair;
|
||||
|
||||
impl Pkg {
|
||||
/// Create a new package intended to serve as the compilation unit,
|
||||
/// with an empty pathspec.
|
||||
pub fn new<S: Into<Span>>(span: S) -> Self {
|
||||
Self(span.into())
|
||||
let s = span.into();
|
||||
Self(s, SPair(WS_EMPTY, s))
|
||||
}
|
||||
|
||||
/// Represent a package imported according to the provided
|
||||
/// [`PathSpec`].
|
||||
pub fn new_imported(pathspec: PathSpec) -> Self {
|
||||
Self(pathspec.span(), pathspec)
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
Self(span) => *span,
|
||||
Self(span, _) => *span,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pathspec(&self) -> PathSpec {
|
||||
match self {
|
||||
Self(_, pathspec) => *pathspec,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +79,7 @@ impl Display for Pkg {
|
|||
impl Functor<Span> for Pkg {
|
||||
fn map(self, f: impl FnOnce(Span) -> Span) -> Self::Target {
|
||||
match self {
|
||||
Self(span) => Self(f(span)),
|
||||
Self(span, path) => Self(f(span), path),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +90,10 @@ object_rel! {
|
|||
///
|
||||
/// Imported [`Ident`]s do not have edges from this package.
|
||||
Pkg -> {
|
||||
// Package import
|
||||
cross Pkg,
|
||||
|
||||
// Identified objects owned by this package.
|
||||
tree Ident,
|
||||
|
||||
// Anonymous templates are used for expansion.
|
||||
|
@ -76,4 +106,14 @@ impl ObjectIndex<Pkg> {
|
|||
pub fn close(self, asg: &mut Asg, span: Span) -> Self {
|
||||
self.map_obj(asg, Pkg::fmap(|open| open.merge(span).unwrap_or(open)))
|
||||
}
|
||||
|
||||
/// Indicate that a package should be imported at the provided
|
||||
/// pathspec.
|
||||
///
|
||||
/// This simply adds the import to the graph;
|
||||
/// package loading must be performed by another subsystem.
|
||||
pub fn import(self, asg: &mut Asg, pathspec: SPair) -> Self {
|
||||
let oi_import = asg.create(Pkg::new_imported(pathspec));
|
||||
self.add_edge_to(asg, oi_import, Some(pathspec.span()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -176,7 +176,14 @@ impl<'a> TreeContext<'a> {
|
|||
let paired_rel = dyn_rel.resolve_oi_pairs(self.asg);
|
||||
|
||||
match paired_rel.target() {
|
||||
Object::Pkg((pkg, _)) => self.emit_package(pkg, depth),
|
||||
Object::Pkg((pkg, oi_pkg)) => match paired_rel.source() {
|
||||
Object::Root(_) => self.emit_package(pkg, depth),
|
||||
Object::Pkg(_) => self.emit_import(pkg, depth),
|
||||
_ => diagnostic_panic!(
|
||||
vec![oi_pkg.error("package was not expected here")],
|
||||
"invalid context for package object during xmli derivation",
|
||||
),
|
||||
},
|
||||
|
||||
// Identifiers will be considered in context;
|
||||
// pass over it for now.
|
||||
|
@ -223,6 +230,18 @@ impl<'a> TreeContext<'a> {
|
|||
Some(package(pkg, depth))
|
||||
}
|
||||
|
||||
/// Emit a package import statement.
|
||||
fn emit_import(&mut self, pkg: &Pkg, depth: Depth) -> Option<Xirf> {
|
||||
let ps = pkg.pathspec();
|
||||
self.push(Xirf::attr(QN_PACKAGE, ps.symbol(), (ps.span(), ps.span())));
|
||||
|
||||
Some(Xirf::open(
|
||||
QN_IMPORT,
|
||||
OpenSpan::without_name_span(pkg.span()),
|
||||
depth,
|
||||
))
|
||||
}
|
||||
|
||||
/// Emit an expression as a legacy TAME statement or expression.
|
||||
///
|
||||
/// Identified expressions must be represented using statements in
|
||||
|
|
|
@ -260,7 +260,7 @@ ele_parse! {
|
|||
/// for composing larger systems out of smaller components.
|
||||
ImportStmt := QN_IMPORT {
|
||||
@ {
|
||||
QN_PACKAGE => TodoAttr,
|
||||
QN_PACKAGE => Ref,
|
||||
QN_EXPORT => TodoAttr,
|
||||
} => Todo,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
<package xmlns="http://www.lovullo.com/rater"
|
||||
xmlns:c="http://www.lovullo.com/calc"
|
||||
xmlns:t="http://www.lovullo.com/rater/apply-template">
|
||||
|
||||
|
||||
|
||||
<import package="first" />
|
||||
<import package="second" />
|
||||
<import package="third" />
|
||||
</package>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0"?>
|
||||
<package xmlns="http://www.lovullo.com/rater"
|
||||
xmlns:c="http://www.lovullo.com/calc"
|
||||
xmlns:t="http://www.lovullo.com/rater/apply-template">
|
||||
|
||||
Packages aren't actually imported yet,
|
||||
but they do need to be represented on the graph for `xmli` derivation.
|
||||
<import package="first" />
|
||||
<import package="second" />
|
||||
<import package="third" />
|
||||
</package>
|
||||
|
Loading…
Reference in New Issue