tamer: asg::graph::object::xir: POC use of token stack

Just some final POC setup for how this'll work; it's nothing
significant.  This just emits an `@xmlns` on the `package` element to
demonstrate use of the stack.

With that, it's time to formalize this.

I also need to document at some point why I choose to use `ArrayVec` still
over `Vec`---it's not a microoptimization.  It's intended to simplify the
runtime to keep execution simple with fewer code paths and make it more
amenable to analysis.  Memory allocation is a pretty complex thing and
muddies execution.  It's also another point of failure, though practically
speaking, I'm not worried about that---this is replacing a system that
consumes many GiB of memory (XSLT-based compiler) with one that consumes 10s
of MiB.

DEV-13708
main
Mike Gerwitz 2023-02-22 10:45:48 -05:00
parent 7efd08a699
commit 716247483f
1 changed files with 45 additions and 15 deletions

View File

@ -29,21 +29,22 @@
//! but may be useful in the future for concrete code suggestions/fixes,
//! or observing template expansions.
use std::{convert::Infallible, fmt::Display, marker::PhantomData};
use super::ObjectRelTy;
use crate::{
asg::{visit::TreeWalkRel, Asg},
diagnose::Annotate,
diagnostic_unreachable,
parse::prelude::*,
sym::st::raw::URI_LV_RATER,
xir::{
attr::Attr,
flat::{Text, XirfToken},
st::qname::QN_PACKAGE,
st::qname::{QN_PACKAGE, QN_XMLNS},
OpenSpan,
},
};
use super::ObjectRelTy;
use arrayvec::ArrayVec;
use std::{convert::Infallible, fmt::Display, marker::PhantomData};
#[derive(Debug, PartialEq, Eq)]
pub enum AsgTreeToXirf<'a> {
@ -71,23 +72,34 @@ impl<'a> ParseState for AsgTreeToXirf<'a> {
fn parse_token(
self,
tok: Self::Token,
TreeContext(_, asg): &mut TreeContext,
TreeContext(tok_stack, asg): &mut TreeContext,
) -> TransitionResult<Self::Super> {
use ObjectRelTy as Ty;
if let Some(emit) = tok_stack.pop() {
return Transition(self).ok(emit).with_lookahead(tok);
}
let tok_span = tok.span();
let TreeWalkRel(dyn_rel, depth) = tok;
// POC: Read-only access to the ASG for resolving edge refs.
let obj = dyn_rel.target().resolve(asg);
let obj_span = obj.span();
match dyn_rel.target_ty() {
Ty::Pkg => Transition(self).ok(XirfToken::Open(
QN_PACKAGE,
OpenSpan::without_name_span(obj_span),
depth,
)),
Ty::Pkg => {
tok_stack.push(XirfToken::Attr(Attr::new(
QN_XMLNS,
URI_LV_RATER,
(obj_span, obj_span),
)));
Transition(self).ok(XirfToken::Open(
QN_PACKAGE,
OpenSpan::without_name_span(obj_span),
depth,
))
}
Ty::Ident | Ty::Expr => Transition(self).incomplete(),
@ -103,10 +115,28 @@ impl<'a> ParseState for AsgTreeToXirf<'a> {
}
}
#[derive(Debug)]
pub struct TreeContext<'a>(StackPlaceholder, &'a Asg);
/// Size of the token stack.
///
/// See [`TokenStack`] for more information.
const TOK_STACK_SIZE: usize = 8;
type StackPlaceholder = ();
/// Token stack to hold generated tokens between [`AsgTreeToXirf`]
/// iterations.
///
/// The token stack is used to avoid having to create separate states for
/// emitting each individual token.
/// It is populated by [`AsgTreeToXirf`] if more than a single [`XirfToken`]
/// needs to be emitted,
/// and tokens are removed on each subsequent iteration until empty.
///
/// This need only be big enough to accommodate [`AsgTreeToXirf`]'s
/// implementation;
/// the size is independent of user input.
type TokenStack<'a> =
ArrayVec<<AsgTreeToXirf<'a> as ParseState>::Object, TOK_STACK_SIZE>;
#[derive(Debug)]
pub struct TreeContext<'a>(TokenStack<'a>, &'a Asg);
impl<'a> From<&'a Asg> for TreeContext<'a> {
fn from(asg: &'a Asg) -> Self {