tamer: asg::air: AirAggregateCtx: New AirAggregate::Context

Future changes to `AirAggregate` are going to require additional context (a
stack, specifically), but the `Context` is currently utilized
by `Asg`.  This introduces a layer of abstraction that will allow us to add
the stack.

Alongside these changes, `ParseState` has been augmented with a `PubContext`
type that is utilized on public APIs, both maintaining BC with existing code
and keeping these implementation details encapsulated.

This does make a bit of a mess of the internal implementation, though, with
`asg_mut()` sprinkled about, so maybe the next commit can clean that up a
bit.  EDIT: After adding `AsMut` to a bunch of asg::graph::object::*
methods, I decided against it, because it messes with the inferred
ownership, requiring explicit borrows via `as_mut()` where they were not
required before.  I think the existing code is easier to reason about than
what would otherwise result from having `mut asg: impl AsMut<Asg>`
everwhere.

DEV-13708
main
Mike Gerwitz 2023-03-27 11:47:11 -04:00
parent fc569f7551
commit b1ce7aaf29
6 changed files with 117 additions and 50 deletions

View File

@ -107,16 +107,17 @@ impl ParseState for AirAggregate {
type Token = Air; type Token = Air;
type Object = (); type Object = ();
type Error = AsgError; type Error = AsgError;
type Context = AirAggregateCtx;
/// Destination [`Asg`] that this parser lowers into. /// Destination [`Asg`] that this parser lowers into.
/// ///
/// This ASG will be yielded by [`crate::parse::Parser::finalize`]. /// This ASG will be yielded by [`crate::parse::Parser::finalize`].
type Context = Asg; type PubContext = Asg;
fn parse_token( fn parse_token(
self, self,
tok: Self::Token, tok: Self::Token,
asg: &mut Self::Context, ctx: &mut Self::Context,
) -> crate::parse::TransitionResult<Self> { ) -> crate::parse::TransitionResult<Self> {
use ir::{ use ir::{
AirBind::*, AirIdent::*, AirPkg::*, AirSubsets::*, AirTodo::*, AirBind::*, AirIdent::*, AirPkg::*, AirSubsets::*, AirTodo::*,
@ -128,7 +129,8 @@ impl ParseState for AirAggregate {
(st, AirTodo(Todo(_))) => Transition(st).incomplete(), (st, AirTodo(Todo(_))) => Transition(st).incomplete(),
(Empty, AirPkg(PkgStart(span))) => { (Empty, AirPkg(PkgStart(span))) => {
let oi_pkg = asg.create(Pkg::new(span)).root(asg); let oi_pkg =
ctx.asg_mut().create(Pkg::new(span)).root(ctx.asg_mut());
Transition(Toplevel(oi_pkg, AirExprAggregate::new_in(oi_pkg))) Transition(Toplevel(oi_pkg, AirExprAggregate::new_in(oi_pkg)))
.incomplete() .incomplete()
} }
@ -145,7 +147,7 @@ impl ParseState for AirAggregate {
// No expression was started. // No expression was started.
(Toplevel(oi_pkg, _expr), AirPkg(PkgEnd(span))) => { (Toplevel(oi_pkg, _expr), AirPkg(PkgEnd(span))) => {
oi_pkg.close(asg, span); oi_pkg.close(ctx.asg_mut(), span);
Transition(Empty).incomplete() Transition(Empty).incomplete()
} }
@ -182,21 +184,21 @@ impl ParseState for AirAggregate {
// unreachable paths) // unreachable paths)
// because of the different inner types. // because of the different inner types.
(PkgExpr(oi_pkg, expr), AirExpr(etok)) => { (PkgExpr(oi_pkg, expr), AirExpr(etok)) => {
Self::delegate_expr(asg, oi_pkg, expr, etok) Self::delegate_expr(ctx, oi_pkg, expr, etok)
} }
(PkgExpr(oi_pkg, expr), AirBind(etok)) => { (PkgExpr(oi_pkg, expr), AirBind(etok)) => {
Self::delegate_expr(asg, oi_pkg, expr, etok) Self::delegate_expr(ctx, oi_pkg, expr, etok)
} }
// Template parsing. // Template parsing.
(PkgTpl(oi_pkg, stored_expr, tplst), AirExpr(ttok)) => { (PkgTpl(oi_pkg, stored_expr, tplst), AirExpr(ttok)) => {
Self::delegate_tpl(asg, oi_pkg, stored_expr, tplst, ttok) Self::delegate_tpl(ctx, oi_pkg, stored_expr, tplst, ttok)
} }
(PkgTpl(oi_pkg, stored_expr, tplst), AirBind(ttok)) => { (PkgTpl(oi_pkg, stored_expr, tplst), AirBind(ttok)) => {
Self::delegate_tpl(asg, oi_pkg, stored_expr, tplst, ttok) Self::delegate_tpl(ctx, oi_pkg, stored_expr, tplst, ttok)
} }
(PkgTpl(oi_pkg, stored_expr, tplst), AirTpl(ttok)) => { (PkgTpl(oi_pkg, stored_expr, tplst), AirTpl(ttok)) => {
Self::delegate_tpl(asg, oi_pkg, stored_expr, tplst, ttok) Self::delegate_tpl(ctx, oi_pkg, stored_expr, tplst, ttok)
} }
(PkgTpl(..), tok @ AirPkg(PkgStart(..))) => { (PkgTpl(..), tok @ AirPkg(PkgStart(..))) => {
@ -211,10 +213,10 @@ impl ParseState for AirAggregate {
} }
(PkgExpr(oi_pkg, expr), AirPkg(PkgEnd(span))) => { (PkgExpr(oi_pkg, expr), AirPkg(PkgEnd(span))) => {
match expr.is_accepting(asg) { match expr.is_accepting(ctx) {
true => { true => {
// TODO: this is duplicated with the above // TODO: this is duplicated with the above
oi_pkg.close(asg, span); oi_pkg.close(ctx.asg_mut(), span);
Transition(Empty).incomplete() Transition(Empty).incomplete()
} }
false => Transition(PkgExpr(oi_pkg, expr)) false => Transition(PkgExpr(oi_pkg, expr))
@ -223,7 +225,7 @@ impl ParseState for AirAggregate {
} }
(PkgTpl(oi_pkg, stored_expr, tplst), AirPkg(PkgEnd(span))) => { (PkgTpl(oi_pkg, stored_expr, tplst), AirPkg(PkgEnd(span))) => {
match tplst.is_accepting(asg) { match tplst.is_accepting(ctx) {
true => Transition(PkgExpr(oi_pkg, stored_expr)) true => Transition(PkgExpr(oi_pkg, stored_expr))
.incomplete() .incomplete()
.with_lookahead(AirPkg(PkgEnd(span))), .with_lookahead(AirPkg(PkgEnd(span))),
@ -236,25 +238,31 @@ impl ParseState for AirAggregate {
Transition(Empty).err(AsgError::PkgExpected(tok.span())) Transition(Empty).err(AsgError::PkgExpected(tok.span()))
} }
(Empty, AirIdent(IdentDecl(name, kind, src))) => { (Empty, AirIdent(IdentDecl(name, kind, src))) => ctx
asg.declare(name, kind, src).map(|_| ()).transition(Empty) .asg_mut()
} .declare(name, kind, src)
.map(|_| ())
.transition(Empty),
(Empty, AirIdent(IdentExternDecl(name, kind, src))) => asg (Empty, AirIdent(IdentExternDecl(name, kind, src))) => ctx
.asg_mut()
.declare_extern(name, kind, src) .declare_extern(name, kind, src)
.map(|_| ()) .map(|_| ())
.transition(Empty), .transition(Empty),
(Empty, AirIdent(IdentDep(sym, dep))) => { (Empty, AirIdent(IdentDep(sym, dep))) => {
asg.add_dep_lookup_global(sym, dep); ctx.asg_mut().add_dep_lookup_global(sym, dep);
Transition(Empty).incomplete() Transition(Empty).incomplete()
} }
(Empty, AirIdent(IdentFragment(sym, text))) => { (Empty, AirIdent(IdentFragment(sym, text))) => ctx
asg.set_fragment(sym, text).map(|_| ()).transition(Empty) .asg_mut()
} .set_fragment(sym, text)
.map(|_| ())
.transition(Empty),
(Empty, AirIdent(IdentRoot(sym))) => { (Empty, AirIdent(IdentRoot(sym))) => {
let asg = ctx.asg_mut();
let obj = asg.lookup_global_or_missing(sym); let obj = asg.lookup_global_or_missing(sym);
asg.add_root(obj); asg.add_root(obj);
@ -322,5 +330,48 @@ impl AirAggregate {
} }
} }
/// Additional parser context.
///
/// TODO: This was introduced to hold additional context;
/// see future commit.
#[derive(Debug, Default)]
pub struct AirAggregateCtx(Asg);
impl AirAggregateCtx {
fn asg_mut(&mut self) -> &mut Asg {
self.as_mut()
}
}
impl AsRef<Asg> for AirAggregateCtx {
fn as_ref(&self) -> &Asg {
match self {
Self(asg) => asg,
}
}
}
impl AsMut<Asg> for AirAggregateCtx {
fn as_mut(&mut self) -> &mut Asg {
match self {
Self(asg) => asg,
}
}
}
impl From<AirAggregateCtx> for Asg {
fn from(ctx: AirAggregateCtx) -> Self {
match ctx {
AirAggregateCtx(asg) => asg,
}
}
}
impl From<Asg> for AirAggregateCtx {
fn from(asg: Asg) -> Self {
Self(asg)
}
}
#[cfg(test)] #[cfg(test)]
mod test; mod test;

View File

@ -27,6 +27,7 @@ use super::{
Asg, AsgError, ObjectIndex, Asg, AsgError, ObjectIndex,
}, },
ir::AirBindableExpr, ir::AirBindableExpr,
AirAggregateCtx,
}; };
use crate::{ use crate::{
asg::{graph::object::ObjectRelTo, Ident, ObjectKind}, asg::{graph::object::ObjectRelTo, Ident, ObjectKind},
@ -87,17 +88,19 @@ impl<O: ObjectKind, S: RootStrategy<O>> ParseState for AirExprAggregate<O, S> {
type Token = AirBindableExpr; type Token = AirBindableExpr;
type Object = (); type Object = ();
type Error = AsgError; type Error = AsgError;
type Context = Asg; type Context = AirAggregateCtx;
fn parse_token( fn parse_token(
self, self,
tok: Self::Token, tok: Self::Token,
asg: &mut Self::Context, ctx: &mut Self::Context,
) -> crate::parse::TransitionResult<Self::Super> { ) -> crate::parse::TransitionResult<Self::Super> {
use super::ir::{AirBind::*, AirExpr::*}; use super::ir::{AirBind::*, AirExpr::*};
use AirBindableExpr::*; use AirBindableExpr::*;
use AirExprAggregate::*; use AirExprAggregate::*;
let asg = ctx.asg_mut();
match (self, tok) { match (self, tok) {
(Ready(root, es, _), AirExpr(ExprStart(op, span))) => { (Ready(root, es, _), AirExpr(ExprStart(op, span))) => {
let oi = asg.create(Expr::new(op, span)); let oi = asg.create(Expr::new(op, span));

View File

@ -25,7 +25,7 @@ use super::{
super::{graph::object::Tpl, Asg, AsgError, ObjectIndex}, super::{graph::object::Tpl, Asg, AsgError, ObjectIndex},
expr::AirExprAggregateStoreDangling, expr::AirExprAggregateStoreDangling,
ir::AirTemplatable, ir::AirTemplatable,
AirExprAggregate, AirAggregateCtx, AirExprAggregate,
}; };
use crate::{ use crate::{
asg::{ asg::{
@ -244,12 +244,12 @@ where
type Token = AirTemplatable; type Token = AirTemplatable;
type Object = (); type Object = ();
type Error = AsgError; type Error = AsgError;
type Context = Asg; type Context = AirAggregateCtx;
fn parse_token( fn parse_token(
self, self,
tok: Self::Token, tok: Self::Token,
asg: &mut Self::Context, ctx: &mut Self::Context,
) -> TransitionResult<Self::Super> { ) -> TransitionResult<Self::Super> {
use super::ir::{AirBind::*, AirTpl::*}; use super::ir::{AirBind::*, AirTpl::*};
use AirTemplatable::*; use AirTemplatable::*;
@ -257,7 +257,7 @@ where
match (self, tok) { match (self, tok) {
(Ready(ois), AirTpl(TplStart(span))) => { (Ready(ois), AirTpl(TplStart(span))) => {
let oi_tpl = asg.create(Tpl::new(span)); let oi_tpl = ctx.asg_mut().create(Tpl::new(span));
Transition(Toplevel( Transition(Toplevel(
ois, ois,
@ -272,24 +272,27 @@ where
"nested tpl open" "nested tpl open"
), ),
(Toplevel(ois, tpl, expr), AirBind(BindIdent(id))) => asg (Toplevel(ois, tpl, expr), AirBind(BindIdent(id))) => {
.lookup_global_or_missing(id) let asg = ctx.asg_mut();
.bind_definition(asg, id, tpl.oi())
.map(|oi_ident| ois.defines(asg, oi_ident)) asg.lookup_global_or_missing(id)
.map(|_| ()) .bind_definition(asg, id, tpl.oi())
.transition(Toplevel(ois, tpl.identify(id), expr)), .map(|oi_ident| ois.defines(asg, oi_ident))
.map(|_| ())
.transition(Toplevel(ois, tpl.identify(id), expr))
}
(Toplevel(ois, tpl, expr), AirBind(RefIdent(id))) => { (Toplevel(ois, tpl, expr), AirBind(RefIdent(id))) => {
tpl.oi().apply_named_tpl(asg, id); tpl.oi().apply_named_tpl(ctx.asg_mut(), id);
Transition(Toplevel(ois, tpl, expr)).incomplete() Transition(Toplevel(ois, tpl, expr)).incomplete()
} }
(Toplevel(ois, tpl, expr), AirTpl(TplMetaStart(span))) => { (Toplevel(ois, tpl, expr), AirTpl(TplMetaStart(span))) => {
let oi_meta = asg.create(Meta::new_required(span)); let oi_meta = ctx.asg_mut().create(Meta::new_required(span));
Transition(TplMeta(ois, tpl, expr, oi_meta)).incomplete() Transition(TplMeta(ois, tpl, expr, oi_meta)).incomplete()
} }
(TplMeta(ois, tpl, expr, oi_meta), AirTpl(TplMetaEnd(cspan))) => { (TplMeta(ois, tpl, expr, oi_meta), AirTpl(TplMetaEnd(cspan))) => {
oi_meta.close(asg, cspan); oi_meta.close(ctx.asg_mut(), cspan);
Transition(Toplevel(ois, tpl, expr)).incomplete() Transition(Toplevel(ois, tpl, expr)).incomplete()
} }
@ -298,13 +301,13 @@ where
ois, ois,
tpl, tpl,
expr, expr,
oi_meta.assign_lexeme(asg, lexeme), oi_meta.assign_lexeme(ctx.asg_mut(), lexeme),
)) ))
.incomplete() .incomplete()
} }
(TplMeta(ois, 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(ctx.asg_mut(), tpl.oi(), name);
Transition(TplMeta(ois, tpl, expr, oi_meta)).incomplete() Transition(TplMeta(ois, tpl, expr, oi_meta)).incomplete()
} }
@ -347,13 +350,13 @@ where
} }
(Toplevel(ois, tpl, _expr_done), AirTpl(TplEnd(span))) => { (Toplevel(ois, tpl, _expr_done), AirTpl(TplEnd(span))) => {
tpl.close(asg, span).transition(Ready(ois)) tpl.close(ctx.asg_mut(), span).transition(Ready(ois))
} }
(TplExpr(ois, 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(ctx) {
tpl.close(asg, span).transition(Ready(ois)) tpl.close(ctx.asg_mut(), span).transition(Ready(ois))
} else { } else {
Transition(TplExpr(ois, tpl, expr)) Transition(TplExpr(ois, tpl, expr))
.err(AsgError::InvalidTplEndContext(span)) .err(AsgError::InvalidTplEndContext(span))
@ -361,7 +364,7 @@ where
} }
(Toplevel(ois, tpl, expr_done), AirTpl(TplEndRef(span))) => { (Toplevel(ois, tpl, expr_done), AirTpl(TplEndRef(span))) => {
tpl.oi().expand_into(asg, ois.oi_target()); tpl.oi().expand_into(ctx.asg_mut(), ois.oi_target());
Transition(Toplevel(ois, tpl.anonymous_reachable(), expr_done)) Transition(Toplevel(ois, tpl.anonymous_reachable(), expr_done))
.incomplete() .incomplete()
@ -369,7 +372,7 @@ where
} }
(TplExpr(ois, tpl, expr_done), AirTpl(TplEndRef(span))) => { (TplExpr(ois, tpl, expr_done), AirTpl(TplEndRef(span))) => {
tpl.oi().expand_into(asg, ois.oi_target()); tpl.oi().expand_into(ctx.asg_mut(), ois.oi_target());
Transition(TplExpr(ois, tpl.anonymous_reachable(), expr_done)) Transition(TplExpr(ois, tpl.anonymous_reachable(), expr_done))
.incomplete() .incomplete()
@ -379,10 +382,10 @@ where
( (
Toplevel(ois, tpl, expr) | TplExpr(ois, tpl, expr), Toplevel(ois, tpl, expr) | TplExpr(ois, tpl, expr),
AirExpr(etok), AirExpr(etok),
) => Self::delegate_expr(asg, ois, tpl, expr, etok), ) => Self::delegate_expr(ctx, ois, tpl, expr, etok),
(TplExpr(ois, tpl, expr), AirBind(etok)) => { (TplExpr(ois, tpl, expr), AirBind(etok)) => {
Self::delegate_expr(asg, ois, tpl, expr, etok) Self::delegate_expr(ctx, ois, tpl, expr, etok)
} }
(TplExpr(..), AirTpl(TplStart(span))) => { (TplExpr(..), AirTpl(TplStart(span))) => {

View File

@ -138,9 +138,9 @@ where
#[inline] #[inline]
fn lower_with_context<U, E>( fn lower_with_context<U, E>(
&mut self, &mut self,
ctx: impl Into<LS::Context>, ctx: impl Into<LS::PubContext>,
f: impl FnOnce(&mut LowerIter<S, Self, LS, EW>) -> Result<U, E>, f: impl FnOnce(&mut LowerIter<S, Self, LS, EW>) -> Result<U, E>,
) -> Result<(U, LS::Context), E> ) -> Result<(U, LS::PubContext), E>
where where
Self: Iterator<Item = WidenedParsedResult<S, EW>> + Sized, Self: Iterator<Item = WidenedParsedResult<S, EW>> + Sized,
E: Diagnostic + From<FinalizeError>, E: Diagnostic + From<FinalizeError>,

View File

@ -497,9 +497,9 @@ pub struct FinalizedParser<S: ParseState>(S::Context);
impl<S: ParseState> FinalizedParser<S> { impl<S: ParseState> FinalizedParser<S> {
/// Take ownership over the inner [`ParseState::Context`]. /// Take ownership over the inner [`ParseState::Context`].
pub fn into_context(self) -> S::Context { pub fn into_context(self) -> S::PubContext {
match self { match self {
Self(ctx) => ctx, Self(ctx) => ctx.into(),
} }
} }
} }

View File

@ -169,6 +169,16 @@ where
/// otherwise-immutable [`ParseState`]. /// otherwise-immutable [`ParseState`].
type Context: Debug = context::Empty; type Context: Debug = context::Empty;
/// Public representation of [`Self::Context`].
///
/// If [`Self::Context`] holds internal state that isn't intended to be
/// exposed via a public API,
/// this represents the type that should be used in its place.
/// Since public APIs include both input and output,
/// this type must be convertable to _and_ from [`Self::Context`].
type PubContext: Debug + From<Self::Context> + Into<Self::Context> =
Self::Context;
/// Construct a parser with a [`Default`] state. /// Construct a parser with a [`Default`] state.
/// ///
/// Whether this method is helpful or provides any clarity depends on /// Whether this method is helpful or provides any clarity depends on
@ -198,12 +208,12 @@ where
/// see [`Parser::finalize`]. /// see [`Parser::finalize`].
fn parse_with_context<I: TokenStream<Self::Token>>( fn parse_with_context<I: TokenStream<Self::Token>>(
toks: I, toks: I,
ctx: Self::Context, ctx: Self::PubContext,
) -> Parser<Self, I> ) -> Parser<Self, I>
where where
Self: ClosedParseState + Default, Self: ClosedParseState + Default,
{ {
Parser::from((toks, ctx)) Parser::from((toks, ctx.into()))
} }
/// Parse a single [`Token`] and optionally perform a state transition. /// Parse a single [`Token`] and optionally perform a state transition.