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 Object = ();
type Error = AsgError;
type Context = AirAggregateCtx;
/// Destination [`Asg`] that this parser lowers into.
///
/// This ASG will be yielded by [`crate::parse::Parser::finalize`].
type Context = Asg;
type PubContext = Asg;
fn parse_token(
self,
tok: Self::Token,
asg: &mut Self::Context,
ctx: &mut Self::Context,
) -> crate::parse::TransitionResult<Self> {
use ir::{
AirBind::*, AirIdent::*, AirPkg::*, AirSubsets::*, AirTodo::*,
@ -128,7 +129,8 @@ impl ParseState for AirAggregate {
(st, AirTodo(Todo(_))) => Transition(st).incomplete(),
(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)))
.incomplete()
}
@ -145,7 +147,7 @@ impl ParseState for AirAggregate {
// No expression was started.
(Toplevel(oi_pkg, _expr), AirPkg(PkgEnd(span))) => {
oi_pkg.close(asg, span);
oi_pkg.close(ctx.asg_mut(), span);
Transition(Empty).incomplete()
}
@ -182,21 +184,21 @@ impl ParseState for AirAggregate {
// unreachable paths)
// because of the different inner types.
(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)) => {
Self::delegate_expr(asg, oi_pkg, expr, etok)
Self::delegate_expr(ctx, oi_pkg, expr, etok)
}
// Template parsing.
(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)) => {
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)) => {
Self::delegate_tpl(asg, oi_pkg, stored_expr, tplst, ttok)
Self::delegate_tpl(ctx, oi_pkg, stored_expr, tplst, ttok)
}
(PkgTpl(..), tok @ AirPkg(PkgStart(..))) => {
@ -211,10 +213,10 @@ impl ParseState for AirAggregate {
}
(PkgExpr(oi_pkg, expr), AirPkg(PkgEnd(span))) => {
match expr.is_accepting(asg) {
match expr.is_accepting(ctx) {
true => {
// TODO: this is duplicated with the above
oi_pkg.close(asg, span);
oi_pkg.close(ctx.asg_mut(), span);
Transition(Empty).incomplete()
}
false => Transition(PkgExpr(oi_pkg, expr))
@ -223,7 +225,7 @@ impl ParseState for AirAggregate {
}
(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))
.incomplete()
.with_lookahead(AirPkg(PkgEnd(span))),
@ -236,25 +238,31 @@ impl ParseState for AirAggregate {
Transition(Empty).err(AsgError::PkgExpected(tok.span()))
}
(Empty, AirIdent(IdentDecl(name, kind, src))) => {
asg.declare(name, kind, src).map(|_| ()).transition(Empty)
}
(Empty, AirIdent(IdentDecl(name, kind, src))) => ctx
.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)
.map(|_| ())
.transition(Empty),
(Empty, AirIdent(IdentDep(sym, dep))) => {
asg.add_dep_lookup_global(sym, dep);
ctx.asg_mut().add_dep_lookup_global(sym, dep);
Transition(Empty).incomplete()
}
(Empty, AirIdent(IdentFragment(sym, text))) => {
asg.set_fragment(sym, text).map(|_| ()).transition(Empty)
}
(Empty, AirIdent(IdentFragment(sym, text))) => ctx
.asg_mut()
.set_fragment(sym, text)
.map(|_| ())
.transition(Empty),
(Empty, AirIdent(IdentRoot(sym))) => {
let asg = ctx.asg_mut();
let obj = asg.lookup_global_or_missing(sym);
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)]
mod test;

View File

@ -27,6 +27,7 @@ use super::{
Asg, AsgError, ObjectIndex,
},
ir::AirBindableExpr,
AirAggregateCtx,
};
use crate::{
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 Object = ();
type Error = AsgError;
type Context = Asg;
type Context = AirAggregateCtx;
fn parse_token(
self,
tok: Self::Token,
asg: &mut Self::Context,
ctx: &mut Self::Context,
) -> crate::parse::TransitionResult<Self::Super> {
use super::ir::{AirBind::*, AirExpr::*};
use AirBindableExpr::*;
use AirExprAggregate::*;
let asg = ctx.asg_mut();
match (self, tok) {
(Ready(root, es, _), AirExpr(ExprStart(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},
expr::AirExprAggregateStoreDangling,
ir::AirTemplatable,
AirExprAggregate,
AirAggregateCtx, AirExprAggregate,
};
use crate::{
asg::{
@ -244,12 +244,12 @@ where
type Token = AirTemplatable;
type Object = ();
type Error = AsgError;
type Context = Asg;
type Context = AirAggregateCtx;
fn parse_token(
self,
tok: Self::Token,
asg: &mut Self::Context,
ctx: &mut Self::Context,
) -> TransitionResult<Self::Super> {
use super::ir::{AirBind::*, AirTpl::*};
use AirTemplatable::*;
@ -257,7 +257,7 @@ where
match (self, tok) {
(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(
ois,
@ -272,24 +272,27 @@ where
"nested tpl open"
),
(Toplevel(ois, tpl, expr), AirBind(BindIdent(id))) => asg
.lookup_global_or_missing(id)
.bind_definition(asg, id, tpl.oi())
.map(|oi_ident| ois.defines(asg, oi_ident))
.map(|_| ())
.transition(Toplevel(ois, tpl.identify(id), expr)),
(Toplevel(ois, tpl, expr), AirBind(BindIdent(id))) => {
let asg = ctx.asg_mut();
asg.lookup_global_or_missing(id)
.bind_definition(asg, id, tpl.oi())
.map(|oi_ident| ois.defines(asg, oi_ident))
.map(|_| ())
.transition(Toplevel(ois, tpl.identify(id), expr))
}
(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()
}
(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()
}
(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()
}
@ -298,13 +301,13 @@ where
ois,
tpl,
expr,
oi_meta.assign_lexeme(asg, lexeme),
oi_meta.assign_lexeme(ctx.asg_mut(), lexeme),
))
.incomplete()
}
(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()
}
@ -347,13 +350,13 @@ where
}
(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))) => {
// TODO: duplicated with AirAggregate
if expr.is_accepting(asg) {
tpl.close(asg, span).transition(Ready(ois))
if expr.is_accepting(ctx) {
tpl.close(ctx.asg_mut(), span).transition(Ready(ois))
} else {
Transition(TplExpr(ois, tpl, expr))
.err(AsgError::InvalidTplEndContext(span))
@ -361,7 +364,7 @@ where
}
(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))
.incomplete()
@ -369,7 +372,7 @@ where
}
(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))
.incomplete()
@ -379,10 +382,10 @@ where
(
Toplevel(ois, tpl, expr) | TplExpr(ois, tpl, expr),
AirExpr(etok),
) => Self::delegate_expr(asg, ois, tpl, expr, etok),
) => Self::delegate_expr(ctx, ois, tpl, expr, 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))) => {

View File

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

View File

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

View File

@ -169,6 +169,16 @@ where
/// otherwise-immutable [`ParseState`].
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.
///
/// Whether this method is helpful or provides any clarity depends on
@ -198,12 +208,12 @@ where
/// see [`Parser::finalize`].
fn parse_with_context<I: TokenStream<Self::Token>>(
toks: I,
ctx: Self::Context,
ctx: Self::PubContext,
) -> Parser<Self, I>
where
Self: ClosedParseState + Default,
{
Parser::from((toks, ctx))
Parser::from((toks, ctx.into()))
}
/// Parse a single [`Token`] and optionally perform a state transition.