tamer: obj::xmlo::reader: Emit token after symbol dependencies
This will allow a tamec xmlo reading pipeline to stop before fragment loading. DEV-13162main
parent
0b9e91b936
commit
26c4076579
|
@ -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"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
);
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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(),
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue