tamer: asg::air::tpl: Distinct, generalized root and targets

Previously, `AirTplAggregate` worked only in a `Pkg` context, being able to
root `Tpl` `Ident`s in `Pkg` and expand only into `Pkg`.  This still does
the same, but generalizes to allow for different roots and expansion
targets.

This will be utilized to parse nested templates.

DEV-13708
main
Mike Gerwitz 2023-03-24 00:22:22 -04:00
parent e1c8e371d5
commit fc569f7551
4 changed files with 140 additions and 82 deletions

View File

@ -80,7 +80,7 @@ pub enum AirAggregate {
PkgTpl( PkgTpl(
ObjectIndex<Pkg>, ObjectIndex<Pkg>,
AirExprAggregateReachable<Pkg>, AirExprAggregateReachable<Pkg>,
AirTplAggregate, AirTplAggregate<Pkg>,
), ),
} }
@ -171,7 +171,7 @@ impl ParseState for AirAggregate {
) => Transition(PkgTpl( ) => Transition(PkgTpl(
oi_pkg, oi_pkg,
expr, expr,
AirTplAggregate::new_in_pkg(oi_pkg), AirTplAggregate::new(oi_pkg, oi_pkg),
)) ))
.incomplete() .incomplete()
.with_lookahead(tok), .with_lookahead(tok),
@ -305,8 +305,8 @@ impl AirAggregate {
asg: &mut <Self as ParseState>::Context, asg: &mut <Self as ParseState>::Context,
oi_pkg: ObjectIndex<Pkg>, oi_pkg: ObjectIndex<Pkg>,
stored_expr: AirExprAggregateReachable<Pkg>, stored_expr: AirExprAggregateReachable<Pkg>,
tplst: AirTplAggregate, tplst: AirTplAggregate<Pkg>,
ttok: impl Into<<AirTplAggregate as ParseState>::Token>, ttok: impl Into<<AirTplAggregate<Pkg> as ParseState>::Token>,
) -> TransitionResult<Self> { ) -> TransitionResult<Self> {
tplst.parse_token(ttok.into(), asg).branch_dead::<Self, _>( tplst.parse_token(ttok.into(), asg).branch_dead::<Self, _>(
|_, stored_expr| { |_, stored_expr| {

View File

@ -22,16 +22,16 @@
//! See the [parent module](super) for more information. //! See the [parent module](super) for more information.
use super::{ use super::{
super::{ super::{graph::object::Tpl, Asg, AsgError, ObjectIndex},
graph::object::{Pkg, Tpl},
Asg, AsgError, ObjectIndex,
},
expr::AirExprAggregateStoreDangling, expr::AirExprAggregateStoreDangling,
ir::AirTemplatable, ir::AirTemplatable,
AirExprAggregate, AirExprAggregate,
}; };
use crate::{ use crate::{
asg::graph::object::Meta, asg::{
graph::object::{Meta, ObjectRelTo},
Ident,
},
diagnose::Annotate, diagnose::Annotate,
diagnostic_todo, diagnostic_todo,
fmt::{DisplayWrapper, TtQuote}, fmt::{DisplayWrapper, TtQuote},
@ -53,7 +53,10 @@ use crate::{
/// expressions just the same as [`super::AirAggregate`] does with /// expressions just the same as [`super::AirAggregate`] does with
/// packages. /// packages.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum AirTplAggregate { pub enum AirTplAggregate<OR, OT = OR>
where
Self: TplEnvCtxPair<OR, OT>,
{
/// Ready for a template, /// Ready for a template,
/// defined as part of the given package. /// defined as part of the given package.
/// ///
@ -62,7 +65,7 @@ pub enum AirTplAggregate {
/// AIR has no restrictions on when template header tokens are /// AIR has no restrictions on when template header tokens are
/// provided, /// provided,
/// which simplifies AIR generation. /// which simplifies AIR generation.
Ready(ObjectIndex<Pkg>), Ready(TplEnvCtx<OR, OT>),
/// Toplevel template context. /// Toplevel template context.
/// ///
@ -71,14 +74,14 @@ pub enum AirTplAggregate {
/// applying to the template itself, /// applying to the template itself,
/// or creating an object directly owned by the template. /// or creating an object directly owned by the template.
Toplevel( Toplevel(
ObjectIndex<Pkg>, TplEnvCtx<OR, OT>,
TplState, TplState,
AirExprAggregateStoreDangling<Tpl>, AirExprAggregateStoreDangling<Tpl>,
), ),
/// Defining a template metavariable. /// Defining a template metavariable.
TplMeta( TplMeta(
ObjectIndex<Pkg>, TplEnvCtx<OR, OT>,
TplState, TplState,
AirExprAggregateStoreDangling<Tpl>, AirExprAggregateStoreDangling<Tpl>,
ObjectIndex<Meta>, ObjectIndex<Meta>,
@ -86,13 +89,16 @@ pub enum AirTplAggregate {
/// Aggregating tokens into a template. /// Aggregating tokens into a template.
TplExpr( TplExpr(
ObjectIndex<Pkg>, TplEnvCtx<OR, OT>,
TplState, TplState,
AirExprAggregateStoreDangling<Tpl>, AirExprAggregateStoreDangling<Tpl>,
), ),
} }
impl Display for AirTplAggregate { impl<OR, OT> Display for AirTplAggregate<OR, OT>
where
Self: TplEnvCtxPair<OR, OT>,
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self { match self {
Self::Ready(_) => write!(f, "ready for template definition"), Self::Ready(_) => write!(f, "ready for template definition"),
@ -108,6 +114,60 @@ impl Display for AirTplAggregate {
} }
} }
/// Environment context for template application.
///
/// The _root_ `OR` represents a container for [`Ident`]s to named templates
/// produced by [`AirTplAggregate`].
///
/// The _expansion target_ `OT` is where template applications will expand
/// into when referenced.
/// Note that templates defined _within_ a template will receive a different
/// expansion target,
/// determined by [`AirTplAggregate``].
#[derive(Debug, PartialEq)]
pub struct TplEnvCtx<OR, OT>(ObjectIndex<OR>, ObjectIndex<OT>)
where
Self: TplEnvCtxPair<OR, OT>;
// At the time of writing (2023-03),
// deriving these macros places `Clone`/`Copy` trait bounds on each of
// `OR` and `OT`,
// which is nonsense.
impl<OR, OT> Clone for TplEnvCtx<OR, OT>
where
Self: TplEnvCtxPair<OR, OT>,
{
fn clone(&self) -> Self {
Self(self.0, self.1)
}
}
impl<OR, OT> Copy for TplEnvCtx<OR, OT> where Self: TplEnvCtxPair<OR, OT> {}
impl<OR, OT> TplEnvCtx<OR, OT>
where
Self: TplEnvCtxPair<OR, OT>,
{
pub fn defines(self, asg: &mut Asg, oi: ObjectIndex<Ident>) -> Self {
match self {
Self(oi_root, oi_target) => {
Self(oi_root.defines(asg, oi), oi_target)
}
}
}
pub fn oi_target(&self) -> ObjectIndex<OT> {
match self {
Self(_, oi_target) => *oi_target,
}
}
}
/// More concisely represent trait bounds for [`TplEnvCtx`].
pub trait TplEnvCtxPair<OR, OT> = Sized
where
OR: ObjectRelTo<Ident>,
OT: ObjectRelTo<Tpl>;
/// The current reachability status of the template. /// The current reachability status of the template.
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum TplState { pub enum TplState {
@ -177,7 +237,10 @@ impl Display for TplState {
} }
} }
impl ParseState for AirTplAggregate { impl<OR, OT> ParseState for AirTplAggregate<OR, OT>
where
Self: TplEnvCtxPair<OR, OT>,
{
type Token = AirTemplatable; type Token = AirTemplatable;
type Object = (); type Object = ();
type Error = AsgError; type Error = AsgError;
@ -193,11 +256,11 @@ impl ParseState for AirTplAggregate {
use AirTplAggregate::*; use AirTplAggregate::*;
match (self, tok) { match (self, tok) {
(Ready(oi_pkg), AirTpl(TplStart(span))) => { (Ready(ois), AirTpl(TplStart(span))) => {
let oi_tpl = asg.create(Tpl::new(span)); let oi_tpl = asg.create(Tpl::new(span));
Transition(Toplevel( Transition(Toplevel(
oi_pkg, ois,
TplState::Dangling(oi_tpl), TplState::Dangling(oi_tpl),
AirExprAggregate::new_in(oi_tpl), AirExprAggregate::new_in(oi_tpl),
)) ))
@ -209,44 +272,40 @@ impl ParseState for AirTplAggregate {
"nested tpl open" "nested tpl open"
), ),
(Toplevel(oi_pkg, tpl, expr), AirBind(BindIdent(id))) => asg (Toplevel(ois, tpl, expr), AirBind(BindIdent(id))) => asg
.lookup_global_or_missing(id) .lookup_global_or_missing(id)
.bind_definition(asg, id, tpl.oi()) .bind_definition(asg, id, tpl.oi())
.map(|oi_ident| oi_pkg.defines(asg, oi_ident)) .map(|oi_ident| ois.defines(asg, oi_ident))
.map(|_| ()) .map(|_| ())
.transition(Toplevel(oi_pkg, tpl.identify(id), expr)), .transition(Toplevel(ois, tpl.identify(id), expr)),
(Toplevel(oi_pkg, tpl, expr), AirBind(RefIdent(id))) => { (Toplevel(ois, tpl, expr), AirBind(RefIdent(id))) => {
tpl.oi().apply_named_tpl(asg, id); tpl.oi().apply_named_tpl(asg, id);
Transition(Toplevel(oi_pkg, tpl, expr)).incomplete() Transition(Toplevel(ois, tpl, expr)).incomplete()
} }
(Toplevel(oi_pkg, tpl, expr), AirTpl(TplMetaStart(span))) => { (Toplevel(ois, tpl, expr), AirTpl(TplMetaStart(span))) => {
let oi_meta = asg.create(Meta::new_required(span)); let oi_meta = asg.create(Meta::new_required(span));
Transition(TplMeta(oi_pkg, tpl, expr, oi_meta)).incomplete() Transition(TplMeta(ois, tpl, expr, oi_meta)).incomplete()
} }
( (TplMeta(ois, tpl, expr, oi_meta), AirTpl(TplMetaEnd(cspan))) => {
TplMeta(oi_pkg, tpl, expr, oi_meta),
AirTpl(TplMetaEnd(cspan)),
) => {
oi_meta.close(asg, cspan); oi_meta.close(asg, cspan);
Transition(Toplevel(oi_pkg, tpl, expr)).incomplete() Transition(Toplevel(ois, tpl, expr)).incomplete()
} }
( (TplMeta(ois, tpl, expr, oi_meta), AirTpl(TplLexeme(lexeme))) => {
TplMeta(oi_pkg, tpl, expr, oi_meta), Transition(TplMeta(
AirTpl(TplLexeme(lexeme)), ois,
) => Transition(TplMeta( tpl,
oi_pkg, expr,
tpl, oi_meta.assign_lexeme(asg, lexeme),
expr, ))
oi_meta.assign_lexeme(asg, lexeme), .incomplete()
)) }
.incomplete(),
(TplMeta(oi_pkg, tpl, expr, oi_meta), AirBind(BindIdent(name))) => { (TplMeta(ois, tpl, expr, oi_meta), AirBind(BindIdent(name))) => {
oi_meta.identify_as_tpl_param(asg, tpl.oi(), name); oi_meta.identify_as_tpl_param(asg, tpl.oi(), name);
Transition(TplMeta(oi_pkg, tpl, expr, oi_meta)).incomplete() Transition(TplMeta(ois, tpl, expr, oi_meta)).incomplete()
} }
(TplMeta(..), tok @ AirBind(RefIdent(..))) => { (TplMeta(..), tok @ AirBind(RefIdent(..))) => {
@ -287,51 +346,43 @@ impl ParseState for AirTplAggregate {
) )
} }
(Toplevel(oi_pkg, tpl, _expr_done), AirTpl(TplEnd(span))) => { (Toplevel(ois, tpl, _expr_done), AirTpl(TplEnd(span))) => {
tpl.close(asg, span).transition(Ready(oi_pkg)) tpl.close(asg, span).transition(Ready(ois))
} }
(TplExpr(oi_pkg, tpl, expr), AirTpl(TplEnd(span))) => { (TplExpr(ois, tpl, expr), AirTpl(TplEnd(span))) => {
// TODO: duplicated with AirAggregate // TODO: duplicated with AirAggregate
if expr.is_accepting(asg) { if expr.is_accepting(asg) {
tpl.close(asg, span).transition(Ready(oi_pkg)) tpl.close(asg, span).transition(Ready(ois))
} else { } else {
Transition(TplExpr(oi_pkg, tpl, expr)) Transition(TplExpr(ois, tpl, expr))
.err(AsgError::InvalidTplEndContext(span)) .err(AsgError::InvalidTplEndContext(span))
} }
} }
(Toplevel(oi_pkg, tpl, expr_done), AirTpl(TplEndRef(span))) => { (Toplevel(ois, tpl, expr_done), AirTpl(TplEndRef(span))) => {
tpl.oi().expand_into(asg, oi_pkg); tpl.oi().expand_into(asg, ois.oi_target());
Transition(Toplevel( Transition(Toplevel(ois, tpl.anonymous_reachable(), expr_done))
oi_pkg, .incomplete()
tpl.anonymous_reachable(), .with_lookahead(AirTpl(TplEnd(span)))
expr_done,
))
.incomplete()
.with_lookahead(AirTpl(TplEnd(span)))
} }
(TplExpr(oi_pkg, tpl, expr_done), AirTpl(TplEndRef(span))) => { (TplExpr(ois, tpl, expr_done), AirTpl(TplEndRef(span))) => {
tpl.oi().expand_into(asg, oi_pkg); tpl.oi().expand_into(asg, ois.oi_target());
Transition(TplExpr( Transition(TplExpr(ois, tpl.anonymous_reachable(), expr_done))
oi_pkg, .incomplete()
tpl.anonymous_reachable(), .with_lookahead(AirTpl(TplEnd(span)))
expr_done,
))
.incomplete()
.with_lookahead(AirTpl(TplEnd(span)))
} }
( (
Toplevel(oi_pkg, tpl, expr) | TplExpr(oi_pkg, tpl, expr), Toplevel(ois, tpl, expr) | TplExpr(ois, tpl, expr),
AirExpr(etok), AirExpr(etok),
) => Self::delegate_expr(asg, oi_pkg, tpl, expr, etok), ) => Self::delegate_expr(asg, ois, tpl, expr, etok),
(TplExpr(oi_pkg, tpl, expr), AirBind(etok)) => { (TplExpr(ois, tpl, expr), AirBind(etok)) => {
Self::delegate_expr(asg, oi_pkg, tpl, expr, etok) Self::delegate_expr(asg, ois, tpl, expr, etok)
} }
(TplExpr(..), AirTpl(TplStart(span))) => { (TplExpr(..), AirTpl(TplStart(span))) => {
@ -366,15 +417,21 @@ impl ParseState for AirTplAggregate {
} }
} }
impl AirTplAggregate { impl<OR, OT> AirTplAggregate<OR, OT>
pub(super) fn new_in_pkg(oi_pkg: ObjectIndex<Pkg>) -> Self { where
Self::Ready(oi_pkg) Self: TplEnvCtxPair<OR, OT>,
{
pub(super) fn new(
oi_root: ObjectIndex<OR>,
oi_target: ObjectIndex<OT>,
) -> Self {
Self::Ready(TplEnvCtx(oi_root, oi_target))
} }
/// Delegate to the expression parser [`AirExprAggregate`]. /// Delegate to the expression parser [`AirExprAggregate`].
fn delegate_expr( fn delegate_expr(
asg: &mut <Self as ParseState>::Context, asg: &mut <Self as ParseState>::Context,
oi_pkg: ObjectIndex<Pkg>, ois: TplEnvCtx<OR, OT>,
tpl: TplState, tpl: TplState,
expr: AirExprAggregateStoreDangling<Tpl>, expr: AirExprAggregateStoreDangling<Tpl>,
etok: impl Into<<AirExprAggregateStoreDangling<Tpl> as ParseState>::Token>, etok: impl Into<<AirExprAggregateStoreDangling<Tpl> as ParseState>::Token>,
@ -382,13 +439,11 @@ impl AirTplAggregate {
let tok = etok.into(); let tok = etok.into();
expr.parse_token(tok, asg).branch_dead::<Self, _>( expr.parse_token(tok, asg).branch_dead::<Self, _>(
|expr, ()| { |expr, ()| Transition(Self::Toplevel(ois, tpl, expr)).incomplete(),
Transition(Self::Toplevel(oi_pkg, tpl, expr)).incomplete()
},
|expr, result, ()| { |expr, result, ()| {
result result
.map(ParseStatus::reflexivity) .map(ParseStatus::reflexivity)
.transition(Self::TplExpr(oi_pkg, tpl, expr)) .transition(Self::TplExpr(ois, tpl, expr))
}, },
(), (),
) )

View File

@ -715,6 +715,14 @@ impl<O: ObjectKind> ObjectIndex<O> {
} }
} }
/// Indicate that the given identifier `oi` is defined by this object.
pub fn defines(self, asg: &mut Asg, oi: ObjectIndex<Ident>) -> Self
where
O: ObjectRelTo<Ident>,
{
self.add_edge_to(asg, oi, None)
}
/// Attempt to look up a locally bound [`Ident`] via a linear search of /// Attempt to look up a locally bound [`Ident`] via a linear search of
/// `self`'s edges. /// `self`'s edges.
/// ///

View File

@ -72,11 +72,6 @@ object_rel! {
} }
impl ObjectIndex<Pkg> { impl ObjectIndex<Pkg> {
/// Indicate that the given identifier `oi` is defined in this package.
pub fn defines(self, asg: &mut Asg, oi: ObjectIndex<Ident>) -> Self {
self.add_edge_to(asg, oi, None)
}
/// Complete the definition of a package. /// Complete the definition of a package.
pub fn close(self, asg: &mut Asg, span: Span) -> Self { pub fn close(self, asg: &mut Asg, span: Span) -> Self {
self.map_obj(asg, Pkg::fmap(|open| open.merge(span).unwrap_or(open))) self.map_obj(asg, Pkg::fmap(|open| open.merge(span).unwrap_or(open)))