tamer: obj::xmlo::reader: Emit token after symbol dependencies

This will allow a tamec xmlo reading pipeline to stop before fragment
loading.

DEV-13162
main
Mike Gerwitz 2023-06-08 08:58:41 -04:00
parent 0b9e91b936
commit 26c4076579
4 changed files with 146 additions and 15 deletions

View File

@ -29,6 +29,7 @@ use fxhash::FxHashSet;
use crate::{
asg::{air::Air, IdentKind, Source},
diagnose::{AnnotatedSpan, Diagnostic},
fmt::{DisplayWrapper, TtQuote},
obj::xmlo::{SymAttrs, SymType},
parse::{util::SPair, ParseState, ParseStatus, Transition, Transitionable},
span::Span,
@ -95,6 +96,7 @@ pub enum XmloToAir {
PackageFound(Span),
Package(PackageSPair),
SymDep(PackageSPair, SPair),
SymDepEnded(PackageSPair, Span),
/// End of header (EOH) reached.
Done(Span),
}
@ -212,13 +214,22 @@ impl ParseState for XmloToAir {
.transition(Package(pkg_name))
}
(Package(pkg_name) | SymDep(pkg_name, _), Fragment(name, text)) => {
(Package(pkg_name) | SymDep(pkg_name, _), SymDepEnd(span)) => {
Transition(SymDepEnded(pkg_name, span)).incomplete()
}
(
Package(pkg_name)
| SymDep(pkg_name, _)
| SymDepEnded(pkg_name, _),
Fragment(name, text),
) => {
Transition(Package(pkg_name)).ok(Air::IdentFragment(name, text))
}
// We don't need to read any further than the end of the
// header (symtable, sym-deps, fragments).
(Package(..) | SymDep(..), Eoh(span)) => {
(Package(..) | SymDep(..) | SymDepEnded(..), Eoh(span)) => {
// It's important to set this _after_ we're done processing,
// otherwise our `first` checks above will be inaccurate.
ctx.first = false;
@ -234,15 +245,36 @@ impl ParseState for XmloToAir {
tok @ (PkgStart(..) | PkgName(..) | Symbol(..)),
) => Transition(st).dead(tok),
(st @ (PackageFound(..) | SymDep(..) | Done(..)), tok) => {
Transition(st).dead(tok)
}
(
st @ (PackageFound(..) | SymDep(..) | SymDepEnded(..)
| Done(..)),
tok,
) => Transition(st).dead(tok),
}
}
fn is_accepting(&self, _: &Self::Context) -> bool {
matches!(*self, Self::Done(_))
}
fn eof_tok(&self, _ctx: &Self::Context) -> Option<Self::Token> {
use XmloToAir::*;
match self {
// We are able to stop parsing immediately after symbol
// dependencies have ended if the caller wishes to ignore
// fragments.
// Pretend that we received an `Eoh` token in this case so that
// we can conclude parsing.
SymDepEnded(_, span) => Some(XmloToken::Eoh(*span)),
Package(_)
| PackageExpected
| PackageFound(_)
| SymDep(_, _)
| Done(_) => None,
}
}
}
impl Display for XmloToAir {
@ -258,6 +290,13 @@ impl Display for XmloToAir {
SymDep(pkg_name, sym) => {
write!(f, "expecting dependency for symbol `/{pkg_name}/{sym}`")
}
SymDepEnded(pkg_name, _) => {
write!(
f,
"expecting fragments or end of header for package {}",
TtQuote::wrap(pkg_name)
)
}
Done(_) => write!(f, "done lowering xmlo into AIR"),
}
}

View File

@ -102,9 +102,10 @@ fn adds_sym_deps() {
PkgName(SPair(name, S2)),
SymDepStart(SPair(sym_from, S3)),
Symbol(SPair(sym_to1, S4)),
Symbol(SPair(sym_to2, S5)),
Eoh(S6),
Symbol(SPair(sym_to1, S4)),
Symbol(SPair(sym_to2, S5)),
SymDepEnd(S6),
Eoh(S7),
];
assert_eq!(
@ -115,7 +116,42 @@ fn adds_sym_deps() {
Incomplete, // SymDepStart
O(Air::IdentDep(SPair(sym_from, S3), SPair(sym_to1, S4))),
O(Air::IdentDep(SPair(sym_from, S3), SPair(sym_to2, S5))),
O(Air::PkgEnd(S6)),
Incomplete, // EndOfDeps
O(Air::PkgEnd(S7)),
]),
Sut::parse(toks.into_iter()).collect(),
);
}
#[test]
fn accepting_state_after_sym_deps() {
let name = "name".into();
let sym_from = "from".into();
let sym_to1 = "to1".into();
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
PkgName(SPair(name, S2)),
SymDepStart(SPair(sym_from, S3)),
Symbol(SPair(sym_to1, S4)),
SymDepEnd(S5),
// Missing EOH; this should be a valid accepting state so that
// parsing can end early.
];
assert_eq!(
#[rustfmt::skip]
Ok(vec![
Incomplete, // PkgStart
O(Air::PkgStart(S1, SPair(name, S2))),
Incomplete, // SymDepStart
O(Air::IdentDep(SPair(sym_from, S3), SPair(sym_to1, S4))),
Incomplete, // EndOfDeps
// The missing EOH is added automatically.
// TODO: Span of last-encountered token.
O(Air::PkgEnd(S5)),
]),
Sut::parse(toks.into_iter()).collect(),
);

View File

@ -80,6 +80,17 @@ pub enum XmloToken {
/// object file representing the source location of this symbol.
Symbol(SPair),
/// End of symbol dependencies.
///
/// This token indicates that all symbols and their dependencies have
/// been parsed.
/// This is a safe stopping point for subsystems that do not wish to
/// load fragments.
///
/// (This is not named `Eos` because that is not a commonly used
/// initialism and is not clear.)
SymDepEnd(Span),
/// Text (compiled code) fragment for a given symbol.
///
/// This contains the compiler output for a given symbol,
@ -120,6 +131,7 @@ impl Token for XmloToken {
| SymDecl(SPair(_, span), _)
| SymDepStart(SPair(_, span))
| Symbol(SPair(_, span))
| SymDepEnd(span)
| Fragment(SPair(_, span), _)
| Eoh(span) => *span,
}
@ -155,6 +167,7 @@ impl Display for XmloToken {
)
}
Symbol(sym) => write!(f, "symbol {}", TtQuote::wrap(sym)),
SymDepEnd(_) => write!(f, "end of symbol dependencies"),
Fragment(sym, _) => {
write!(f, "symbol {} code fragment", TtQuote::wrap(sym))
}
@ -259,11 +272,11 @@ impl ParseState for XmloReader {
.incomplete()
}
(SymDeps(_, sd), Xirf::Close(None | Some(QN_P_SYM_DEPS), ..))
if sd.is_accepting(ctx) =>
{
Transition(FragmentsExpected).incomplete()
}
(
SymDeps(_, sd),
Xirf::Close(None | Some(QN_P_SYM_DEPS), cspan, _),
) if sd.is_accepting(ctx) => Transition(FragmentsExpected)
.ok(XmloToken::SymDepEnd(cspan.span())),
(SymDeps(span, sd), tok) => sd.delegate(
tok,
@ -308,7 +321,7 @@ impl ParseState for XmloReader {
}
fn is_accepting(&self, _: &Self::Context) -> bool {
*self == Self::Eoh || *self == Self::Done
matches!(self, Self::FragmentsExpected | Self::Eoh | Self::Done)
}
}

View File

@ -701,6 +701,7 @@ fn xmlo_composite_parsers_header() {
O(PkgStart(S1)),
O(SymDecl(SPair(sym_name, S3), Default::default(),)),
O(SymDepStart(SPair(symdep_name, S3))),
O(SymDepEnd(S3)),
O(Fragment(SPair(symfrag_id, S4), frag)),
O(Eoh(S3)),
]),
@ -711,3 +712,45 @@ fn xmlo_composite_parsers_header() {
.collect(),
);
}
#[test]
fn xmlo_end_after_sym_deps_before_fragments() {
let sym_name = "sym".into();
let symdep_name = "symdep".into();
#[rustfmt::skip]
let toks_header = [
open(QN_PACKAGE, S1, Depth(0)),
open(QN_P_SYMTABLE, S2, Depth(1)),
open(QN_P_SYM, S3, Depth(2)),
attr(QN_NAME, sym_name, (S2, S3)),
close_empty(S4, Depth(2)),
close(Some(QN_P_SYMTABLE), S4, Depth(1)),
open(QN_P_SYM_DEPS, S2, Depth(1)),
open(QN_P_SYM_DEP, S3, Depth(3)),
attr(QN_NAME, symdep_name, (S2, S3)),
close(Some(QN_P_SYM_DEP), S4, Depth(3)),
close(Some(QN_P_SYM_DEPS), S3, Depth(1)),
// End before fragments.
]
.into_iter();
let sut = Sut::parse(toks_header);
#[rustfmt::skip]
assert_eq!(
Ok(vec![
O(PkgStart(S1)),
O(SymDecl(SPair(sym_name, S3), Default::default(),)),
O(SymDepStart(SPair(symdep_name, S3))),
O(SymDepEnd(S3)),
]),
sut.filter(|parsed| match parsed {
Ok(Incomplete) => false,
_ => true,
})
.collect(),
);
}