tamer: asg::air: Use StateStack

This was extracted from xir::parse::ele in previous commits.  The
conventions help to ensure that pushing and returns are being performed
correctly.  The abstraction will continue to evolve.

This ends up using `Ready` as the dead state.  I need to determine if this
is ideal, and if so, maybe just use `Default`, otherwise yield an error.

DEV-13708
main
Mike Gerwitz 2023-03-30 15:10:52 -04:00
parent e6c6028b37
commit f29e3cfce1
4 changed files with 60 additions and 57 deletions

View File

@ -40,7 +40,10 @@ use super::{
Asg, AsgError, Expr, Ident, ObjectIndex,
};
use crate::{
diagnose::Annotate, diagnostic_todo, parse::prelude::*, sym::SymbolId,
diagnose::Annotate,
diagnostic_todo,
parse::{prelude::*, StateStack},
sym::SymbolId,
};
use std::fmt::{Debug, Display};
@ -169,23 +172,26 @@ impl ParseState for AirAggregate {
}
(Toplevel(oi_pkg), tok @ AirExpr(..)) => {
ctx.push(Toplevel(oi_pkg));
let expr = AirExprAggregate::new();
Transition(PkgExpr(expr)).incomplete().with_lookahead(tok)
ctx.stack().transfer_with_ret(
Transition(Toplevel(oi_pkg)),
Transition(AirExprAggregate::new())
.incomplete()
.with_lookahead(tok),
)
}
(st @ (Toplevel(_) | PkgExpr(_)), tok @ AirTpl(..)) => {
// TODO: this call/ret needs to be part of `ctx`
// TODO: generalize this construction
if st.active_is_accepting(ctx) {
Transition(ctx.pop().expect("TODO"))
.incomplete()
.with_lookahead(tok)
// TODO: dead state or error
ctx.stack().ret_or_dead(Empty, tok)
} else {
ctx.push(st);
Transition(PkgTpl(AirTplAggregate::new()))
.incomplete()
.with_lookahead(tok)
ctx.stack().transfer_with_ret(
Transition(st),
Transition(PkgTpl(AirTplAggregate::new()))
.incomplete()
.with_lookahead(tok),
)
}
}
@ -223,27 +229,14 @@ impl ParseState for AirAggregate {
Transition(Empty).err(AsgError::InvalidPkgEndContext(span))
}
(PkgExpr(expr), AirPkg(PkgEnd(span))) => {
match expr.is_accepting(ctx) {
(st @ (PkgExpr(_) | PkgTpl(_)), AirPkg(PkgEnd(span))) => {
match st.active_is_accepting(ctx) {
true => {
// TODO: this is duplicated
Transition(ctx.pop().expect("TODO"))
.incomplete()
.with_lookahead(AirPkg(PkgEnd(span)))
ctx.stack().ret_or_dead(Empty, AirPkg(PkgEnd(span)))
}
false => {
Transition(st).err(AsgError::InvalidPkgEndContext(span))
}
false => Transition(PkgExpr(expr))
.err(AsgError::InvalidPkgEndContext(span)),
}
}
(PkgTpl(tplst), AirPkg(PkgEnd(span))) => {
match tplst.is_accepting(ctx) {
// TODO
true => Transition(ctx.pop().expect("TODO"))
.incomplete()
.with_lookahead(AirPkg(PkgEnd(span))),
false => Transition(PkgTpl(tplst))
.err(AsgError::InvalidPkgEndContext(span)),
}
}
@ -302,9 +295,7 @@ impl AirAggregate {
etok: impl Into<<AirExprAggregate as ParseState>::Token>,
) -> TransitionResult<Self> {
expr.delegate_child(etok.into(), ctx, |_deadst, tok, ctx| {
Transition(ctx.pop().expect("TODO"))
.incomplete()
.with_lookahead(tok)
ctx.stack().ret_or_dead(AirAggregate::Empty, tok)
})
}
@ -321,9 +312,7 @@ impl AirAggregate {
ttok: impl Into<<AirTplAggregate as ParseState>::Token>,
) -> TransitionResult<Self> {
tplst.delegate_child(ttok.into(), ctx, |_deadst, tok, ctx| {
Transition(ctx.pop().expect("TODO"))
.incomplete()
.with_lookahead(tok)
ctx.stack().ret_or_dead(AirAggregate::Empty, tok)
})
}
@ -356,24 +345,33 @@ impl AirAggregate {
#[derive(Debug, Default)]
pub struct AirAggregateCtx(Asg, AirStack);
/// Limit of the maximum number of held parser frames.
///
/// Note that this is the number of [`ParseState`]s held,
/// _not_ the depth of the graph at a given point.
/// The intent of this is to limit runaway recursion in the event of some
/// bug in the system;
/// while the input stream is certainly finite,
/// lookahead tokens cause recursion that does not provably
/// terminate.
///
/// This limit is arbitrarily large,
/// but hopefully such that no legitimate case will ever hit it.
const MAX_AIR_STACK_DEPTH: usize = 1024;
/// Held parser stack frames.
///
/// See [`AirAggregateCtx`] for more information.
pub type AirStack = Vec<AirAggregate>;
pub type AirStack = StateStack<AirAggregate, MAX_AIR_STACK_DEPTH>;
impl AirAggregateCtx {
fn asg_mut(&mut self) -> &mut Asg {
self.as_mut()
}
fn push<S: Into<AirAggregate>>(&mut self, st: S) {
fn stack(&mut self) -> &mut AirStack {
let Self(_, stack) = self;
stack.push(st.into());
}
fn pop(&mut self) -> Option<AirAggregate> {
let Self(_, stack) = self;
stack.pop()
stack
}
/// The active container (binding context) for [`Ident`]s.

View File

@ -269,13 +269,12 @@ impl ParseState for AirTplAggregate {
.with_lookahead(AirTpl(TplEnd(span)))
}
(Toplevel(tpl), tok @ AirExpr(_)) => {
ctx.push(Toplevel(tpl));
(Toplevel(tpl), tok @ AirExpr(_)) => ctx.stack().transfer_with_ret(
Transition(Toplevel(tpl)),
Transition(AirExprAggregate::new())
.incomplete()
.with_lookahead(tok)
}
.with_lookahead(tok),
),
(
Ready,

View File

@ -720,8 +720,8 @@ impl<S: ClosedParseState, const MAX_DEPTH: usize> StateStack<S, MAX_DEPTH> {
/// after having completed a full parse.
pub fn ret_or_dead(
&mut self,
lookahead: S::Token,
deadst: S,
lookahead: impl Token + Into<S::Token>,
) -> TransitionResult<S> {
let Self(stack) = self;
@ -735,9 +735,15 @@ impl<S: ClosedParseState, const MAX_DEPTH: usize> StateStack<S, MAX_DEPTH> {
}
}
/// Test every [`ParseState`] on the stack against the predicate `f`.
pub fn all(&self, f: impl Fn(&S) -> bool) -> bool {
/// Iterate through each [`ClosedParseState`] held on the stack.
pub fn iter(&self) -> std::slice::Iter<'_, S> {
let Self(stack) = self;
stack[..].iter().all(f)
stack[..].iter()
}
/// Number of [`ClosedParseState`]s held on the stack.
pub fn len(&self) -> usize {
let Self(stack) = self;
stack.len()
}
}

View File

@ -1203,7 +1203,7 @@ macro_rules! ele_parse {
tok,
stack,
|deadst, tok, stack| {
stack.ret_or_dead(tok, deadst)
stack.ret_or_dead(deadst, tok)
},
),
)*
@ -1227,7 +1227,7 @@ macro_rules! ele_parse {
//
// After having considered the stack,
// we can then consider the active `ParseState`.
stack.all(|st| st.is_inner_accepting(stack))
stack.iter().all(|st| st.is_inner_accepting(stack))
&& self.is_inner_accepting(stack)
}
}