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::{
|
use crate::{
|
||||||
asg::{air::Air, IdentKind, Source},
|
asg::{air::Air, IdentKind, Source},
|
||||||
diagnose::{AnnotatedSpan, Diagnostic},
|
diagnose::{AnnotatedSpan, Diagnostic},
|
||||||
|
fmt::{DisplayWrapper, TtQuote},
|
||||||
obj::xmlo::{SymAttrs, SymType},
|
obj::xmlo::{SymAttrs, SymType},
|
||||||
parse::{util::SPair, ParseState, ParseStatus, Transition, Transitionable},
|
parse::{util::SPair, ParseState, ParseStatus, Transition, Transitionable},
|
||||||
span::Span,
|
span::Span,
|
||||||
|
@ -95,6 +96,7 @@ pub enum XmloToAir {
|
||||||
PackageFound(Span),
|
PackageFound(Span),
|
||||||
Package(PackageSPair),
|
Package(PackageSPair),
|
||||||
SymDep(PackageSPair, SPair),
|
SymDep(PackageSPair, SPair),
|
||||||
|
SymDepEnded(PackageSPair, Span),
|
||||||
/// End of header (EOH) reached.
|
/// End of header (EOH) reached.
|
||||||
Done(Span),
|
Done(Span),
|
||||||
}
|
}
|
||||||
|
@ -212,13 +214,22 @@ impl ParseState for XmloToAir {
|
||||||
.transition(Package(pkg_name))
|
.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))
|
Transition(Package(pkg_name)).ok(Air::IdentFragment(name, text))
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't need to read any further than the end of the
|
// We don't need to read any further than the end of the
|
||||||
// header (symtable, sym-deps, fragments).
|
// 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,
|
// It's important to set this _after_ we're done processing,
|
||||||
// otherwise our `first` checks above will be inaccurate.
|
// otherwise our `first` checks above will be inaccurate.
|
||||||
ctx.first = false;
|
ctx.first = false;
|
||||||
|
@ -234,15 +245,36 @@ impl ParseState for XmloToAir {
|
||||||
tok @ (PkgStart(..) | PkgName(..) | Symbol(..)),
|
tok @ (PkgStart(..) | PkgName(..) | Symbol(..)),
|
||||||
) => Transition(st).dead(tok),
|
) => 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 {
|
fn is_accepting(&self, _: &Self::Context) -> bool {
|
||||||
matches!(*self, Self::Done(_))
|
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 {
|
impl Display for XmloToAir {
|
||||||
|
@ -258,6 +290,13 @@ impl Display for XmloToAir {
|
||||||
SymDep(pkg_name, sym) => {
|
SymDep(pkg_name, sym) => {
|
||||||
write!(f, "expecting dependency for symbol `/{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"),
|
Done(_) => write!(f, "done lowering xmlo into AIR"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,9 +102,10 @@ fn adds_sym_deps() {
|
||||||
PkgName(SPair(name, S2)),
|
PkgName(SPair(name, S2)),
|
||||||
|
|
||||||
SymDepStart(SPair(sym_from, S3)),
|
SymDepStart(SPair(sym_from, S3)),
|
||||||
Symbol(SPair(sym_to1, S4)),
|
Symbol(SPair(sym_to1, S4)),
|
||||||
Symbol(SPair(sym_to2, S5)),
|
Symbol(SPair(sym_to2, S5)),
|
||||||
Eoh(S6),
|
SymDepEnd(S6),
|
||||||
|
Eoh(S7),
|
||||||
];
|
];
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -115,7 +116,42 @@ fn adds_sym_deps() {
|
||||||
Incomplete, // SymDepStart
|
Incomplete, // SymDepStart
|
||||||
O(Air::IdentDep(SPair(sym_from, S3), SPair(sym_to1, S4))),
|
O(Air::IdentDep(SPair(sym_from, S3), SPair(sym_to1, S4))),
|
||||||
O(Air::IdentDep(SPair(sym_from, S3), SPair(sym_to2, S5))),
|
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(),
|
Sut::parse(toks.into_iter()).collect(),
|
||||||
);
|
);
|
||||||
|
|
|
@ -80,6 +80,17 @@ pub enum XmloToken {
|
||||||
/// object file representing the source location of this symbol.
|
/// object file representing the source location of this symbol.
|
||||||
Symbol(SPair),
|
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.
|
/// Text (compiled code) fragment for a given symbol.
|
||||||
///
|
///
|
||||||
/// This contains the compiler output for a given symbol,
|
/// This contains the compiler output for a given symbol,
|
||||||
|
@ -120,6 +131,7 @@ impl Token for XmloToken {
|
||||||
| SymDecl(SPair(_, span), _)
|
| SymDecl(SPair(_, span), _)
|
||||||
| SymDepStart(SPair(_, span))
|
| SymDepStart(SPair(_, span))
|
||||||
| Symbol(SPair(_, span))
|
| Symbol(SPair(_, span))
|
||||||
|
| SymDepEnd(span)
|
||||||
| Fragment(SPair(_, span), _)
|
| Fragment(SPair(_, span), _)
|
||||||
| Eoh(span) => *span,
|
| Eoh(span) => *span,
|
||||||
}
|
}
|
||||||
|
@ -155,6 +167,7 @@ impl Display for XmloToken {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Symbol(sym) => write!(f, "symbol {}", TtQuote::wrap(sym)),
|
Symbol(sym) => write!(f, "symbol {}", TtQuote::wrap(sym)),
|
||||||
|
SymDepEnd(_) => write!(f, "end of symbol dependencies"),
|
||||||
Fragment(sym, _) => {
|
Fragment(sym, _) => {
|
||||||
write!(f, "symbol {} code fragment", TtQuote::wrap(sym))
|
write!(f, "symbol {} code fragment", TtQuote::wrap(sym))
|
||||||
}
|
}
|
||||||
|
@ -259,11 +272,11 @@ impl ParseState for XmloReader {
|
||||||
.incomplete()
|
.incomplete()
|
||||||
}
|
}
|
||||||
|
|
||||||
(SymDeps(_, sd), Xirf::Close(None | Some(QN_P_SYM_DEPS), ..))
|
(
|
||||||
if sd.is_accepting(ctx) =>
|
SymDeps(_, sd),
|
||||||
{
|
Xirf::Close(None | Some(QN_P_SYM_DEPS), cspan, _),
|
||||||
Transition(FragmentsExpected).incomplete()
|
) if sd.is_accepting(ctx) => Transition(FragmentsExpected)
|
||||||
}
|
.ok(XmloToken::SymDepEnd(cspan.span())),
|
||||||
|
|
||||||
(SymDeps(span, sd), tok) => sd.delegate(
|
(SymDeps(span, sd), tok) => sd.delegate(
|
||||||
tok,
|
tok,
|
||||||
|
@ -308,7 +321,7 @@ impl ParseState for XmloReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_accepting(&self, _: &Self::Context) -> bool {
|
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(PkgStart(S1)),
|
||||||
O(SymDecl(SPair(sym_name, S3), Default::default(),)),
|
O(SymDecl(SPair(sym_name, S3), Default::default(),)),
|
||||||
O(SymDepStart(SPair(symdep_name, S3))),
|
O(SymDepStart(SPair(symdep_name, S3))),
|
||||||
|
O(SymDepEnd(S3)),
|
||||||
O(Fragment(SPair(symfrag_id, S4), frag)),
|
O(Fragment(SPair(symfrag_id, S4), frag)),
|
||||||
O(Eoh(S3)),
|
O(Eoh(S3)),
|
||||||
]),
|
]),
|
||||||
|
@ -711,3 +712,45 @@ fn xmlo_composite_parsers_header() {
|
||||||
.collect(),
|
.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