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-13708main
parent
e6c6028b37
commit
f29e3cfce1
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue