tamer: obj::xmlo::reader: preproc:sym-deps processing

This parses the symbol dependency list (adjacency list).

I'm noticing some glaring issues in error handling, particularly that the
token being parsed while an error occurs is not returned and so recovery is
impossible.  I'll have to address that later on, after I get this parser
completed.

Another previous question that I had a hard time answering in prior months
was how I was going to compose boilerplate parsers, e.g. handling the
parsing of single-attribute elements and such.  A pattern is clearly taking
shape, and with the composition of parsers more formalized, that'll be able
to be abstracted away.  But again, that's going to wait until after this
parser is actually functioning.  Too many delays so far.

DEV-10863
main
Mike Gerwitz 2022-03-30 15:03:50 -04:00
parent 3f8e397e57
commit fb3da09fa4
8 changed files with 308 additions and 144 deletions

View File

@ -205,11 +205,11 @@ where
// Unused
}
(IS::None | IS::SymDep(_), XmloEvent::SymDepStart(sym)) => {
(IS::None | IS::SymDep(_), XmloEvent::SymDepStart(sym, _)) => {
istate = IS::SymDep(sym);
}
(IS::SymDep(sym), XmloEvent::Symbol(dep_sym)) => {
(IS::SymDep(sym), XmloEvent::Symbol(dep_sym, _)) => {
self.add_dep_lookup(sym, dep_sym);
}
@ -361,7 +361,7 @@ mod test {
use super::*;
use crate::asg::{DefaultAsg, FragmentText, IdentObject};
use crate::obj::xmlo::{SymAttrs, SymType};
use crate::span::UNKNOWN_SPAN;
use crate::span::{DUMMY_SPAN, UNKNOWN_SPAN};
use crate::sym::GlobalSymbolIntern;
use std::collections::hash_map::RandomState;
@ -425,9 +425,9 @@ mod test {
let sym_to2 = "to2".intern();
let evs = vec![
Ok(XmloEvent::SymDepStart(sym_from)),
Ok(XmloEvent::Symbol(sym_to1)),
Ok(XmloEvent::Symbol(sym_to2)),
Ok(XmloEvent::SymDepStart(sym_from, DUMMY_SPAN)),
Ok(XmloEvent::Symbol(sym_to1, DUMMY_SPAN)),
Ok(XmloEvent::Symbol(sym_to2, DUMMY_SPAN)),
];
let _ = sut

View File

@ -52,14 +52,14 @@ pub enum XmloError {
/// The provided `preproc:sym/@dim` is invalid.
InvalidDim(SymbolId, Span),
/// A `preproc:sym-dep` element was found, but is missing `@name`.
UnassociatedSymDep,
UnassociatedSymDep(Span),
/// The `preproc:sym[@type="map"]` is missing a @name.
MapFromNameMissing(SymbolId, Span),
/// Multiple `preproc:from` nodes found.
MapFromMultiple(SymbolId, Span),
/// Invalid dependency in adjacency list
/// (`preproc:sym-dep/preproc:sym-ref`).
MalformedSymRef(String),
MalformedSymRef(SymbolId, Span),
/// A `preproc:fragment` element was found, but is missing `@id`.
UnassociatedFragment,
/// A `preproc:fragment` element was found, but is missing `text()`.
@ -116,12 +116,17 @@ impl Display for XmloError {
only once for symbol `{sym}` at {span}"
)
}
Self::UnassociatedSymDep => write!(
Self::UnassociatedSymDep(span) => write!(
fmt,
"unassociated dependency list: preproc:sym-dep/@name missing"
"unassociated dependency list: preproc:sym-dep/@name \
missing at {span}"
),
Self::MalformedSymRef(msg) => {
write!(fmt, "malformed dependency ref: {}", msg)
Self::MalformedSymRef(name, span) => {
write!(
fmt,
"malformed dependency ref for symbol \
{name} at {span}"
)
}
Self::UnassociatedFragment => write!(
fmt,

View File

@ -66,11 +66,11 @@ pub enum XmloEvent {
/// Begin adjacency list for a given symbol and interpret subsequent
/// symbols as edges (dependencies).
SymDepStart(SymbolId),
SymDepStart(SymbolId, Span),
/// A symbol reference whose interpretation is dependent on the current
/// state.
Symbol(SymbolId),
Symbol(SymbolId, Span),
/// Text (compiled code) fragment for a given symbol.
///
@ -103,6 +103,7 @@ qname_const! {
QN_DTYPE: :L_DTYPE,
QN_ELIG_CLASS_YIELDS: L_PREPROC:L_ELIG_CLASS_YIELDS,
QN_EXTERN: :L_EXTERN,
QN_FROM: L_PREPROC:L_FROM,
QN_GENERATED: L_PREPROC:L_GENERATED,
QN_ISOVERRIDE: :L_ISOVERRIDE,
QN_LV_PACKAGE: L_LV:L_PACKAGE,
@ -113,31 +114,44 @@ qname_const! {
QN_SRC: :L_SRC,
QN_SYM: L_PREPROC:L_SYM,
QN_SYMTABLE: L_PREPROC:L_SYMTABLE,
QN_SYM_DEPS: L_PREPROC:L_SYM_DEPS,
QN_SYM_DEP: L_PREPROC:L_SYM_DEP,
QN_SYM_REF: L_PREPROC:L_SYM_REF,
QN_TYPE: :L_TYPE,
QN_UUROOTPATH: :L_UUROOTPATH,
QN_VIRTUAL: :L_VIRTUAL,
QN_YIELDS: :L_YIELDS,
QN_FROM: L_PREPROC:L_FROM,
}
pub trait XmloSymtableState =
ParseState<Token = Xirf, Object = (SymbolId, SymAttrs, Span)>
where <Self as ParseState>::Error: Into<XmloError>;
/// A parser capable of being composed with [`XmloReaderState`].
pub trait XmloState = ParseState<Token = Xirf>
where
<Self as ParseState>::Error: Into<XmloError>,
<Self as ParseState>::Object: Into<XmloEvent>;
#[derive(Debug, Default, PartialEq, Eq)]
pub enum XmloReaderState<SS: XmloSymtableState = SymtableState> {
pub enum XmloReaderState<
SS: XmloState = SymtableState,
SD: XmloState = SymDepsState,
> {
/// Parser has not yet processed any input.
#[default]
Ready,
/// Processing `package` attributes.
Package,
/// Expecting a symbol declaration or end of symbol table.
/// Expecting a symbol declaration or closing `preproc:symtable`.
Symtable(Span, SS),
/// Symbol dependencies are expected next.
SymDepsExpected,
/// Expecting symbol dependency list or closing `preproc:sym-deps`.
SymDeps(Span, SD),
/// End of header parsing.
Eoh,
/// `xmlo` file has been fully read.
Done,
}
impl<SS: XmloSymtableState> ParseState for XmloReaderState<SS> {
impl<SS: XmloState, SD: XmloState> ParseState for XmloReaderState<SS, SD> {
type Token = Xirf;
type Object = XmloEvent;
type Error = XmloError;
@ -177,19 +191,35 @@ impl<SS: XmloSymtableState> ParseState for XmloReaderState<SS> {
(Symtable(_, ss), Xirf::Close(Some(QN_SYMTABLE), ..))
if ss.is_accepting() =>
{
Transition(Done).incomplete()
Transition(SymDepsExpected).incomplete()
}
// TOOD: It'd be nice to augment errors with the symbol table
// span as well (e.g. "while processing symbol table at <loc>").
(Symtable(span, ss), tok) => ss.delegate(span, tok, Symtable),
(SymDepsExpected, Xirf::Open(QN_SYM_DEPS, span, _)) => {
Transition(SymDeps(span, SD::default())).incomplete()
}
(SymDeps(_, sd), Xirf::Close(Some(QN_SYM_DEPS), ..))
if sd.is_accepting() =>
{
Transition(Eoh).incomplete()
}
(SymDeps(span, sd), tok) => sd.delegate(span, tok, SymDeps),
(Eoh, Xirf::Close(Some(QN_PACKAGE), ..)) => {
Transition(Done).incomplete()
}
todo => todo!("{todo:?}"),
}
}
fn is_accepting(&self) -> bool {
*self == Self::Done
*self == Self::Eoh || *self == Self::Done
}
}
@ -427,5 +457,75 @@ impl From<(SymbolId, SymAttrs, Span)> for XmloEvent {
}
}
/// Symbol dependency list (graph adjacency list) parser for
/// `preproc:sym-deps` children.
///
/// This parser expects a parent [`ParseState`] to indicate when dependency
/// parsing ought to start and end—
/// this parser does not recognize any opening or closing
/// `preproc:sym-deps` tags.
#[derive(Debug, Default, PartialEq, Eq)]
pub enum SymDepsState {
/// Symbol table declaration found;
/// symbols declarations expected.
#[default]
Ready,
SymUnnamed(Span),
Sym(Span, SymbolId),
SymRefUnnamed(Span, SymbolId, Span),
SymRefDone(Span, SymbolId, Span),
}
impl ParseState for SymDepsState {
type Token = Xirf;
type Object = XmloEvent;
type Error = XmloError;
fn parse_token(self, tok: Self::Token) -> TransitionResult<Self> {
use SymDepsState::*;
match (self, tok) {
(Ready, Xirf::Open(QN_SYM_DEP, span, _)) => {
Transition(SymUnnamed(span)).incomplete()
}
(SymUnnamed(span), Xirf::Attr(Attr(QN_NAME, name, _))) => {
Transition(Sym(span, name))
.ok(XmloEvent::SymDepStart(name, span))
}
(SymUnnamed(span), _) => Transition(SymUnnamed(span))
.err(XmloError::UnassociatedSymDep(span)),
(Sym(span, name), Xirf::Open(QN_SYM_REF, span_ref, _)) => {
Transition(SymRefUnnamed(span, name, span_ref)).incomplete()
}
(
SymRefUnnamed(span, name, span_ref),
Xirf::Attr(Attr(QN_NAME, ref_name, (_, span_ref_name))),
) => Transition(SymRefDone(span, name, span_ref))
.ok(XmloEvent::Symbol(ref_name, span_ref_name)),
(SymRefUnnamed(span, name, span_ref), _) => {
Transition(SymRefUnnamed(span, name, span_ref))
.err(XmloError::MalformedSymRef(name, span_ref))
}
(SymRefDone(span, name, _), Xirf::Close(..)) => {
Transition(Sym(span, name)).incomplete()
}
(Sym(..), Xirf::Close(..)) => Transition(Ready).incomplete(),
todo => todo!("sym-deps {todo:?}"),
}
}
fn is_accepting(&self) -> bool {
*self == Self::Ready
}
}
#[cfg(test)]
mod test;

View File

@ -510,11 +510,12 @@ where
.with_checks(false)
.filter_map(Result::ok)
.find(|attr| attr.key == b"name")
.map_or(Err(XmloError::UnassociatedSymDep), |attr| {
Ok(unsafe { attr.value.intern_utf8_unchecked() })
})?;
.map_or(
Err(XmloError::UnassociatedSymDep(UNKNOWN_SPAN)),
|attr| Ok(unsafe { attr.value.intern_utf8_unchecked() }),
)?;
event_queue.push_back(XmloEvent::SymDepStart(name));
event_queue.push_back(XmloEvent::SymDepStart(name, UNKNOWN_SPAN));
loop {
match reader.read_event(buffer)? {
@ -529,7 +530,7 @@ where
.find(|attr| attr.key == b"name")
.map_or(
Err(XmloError::MalformedSymRef(
"preproc:sym-ref/@name missing".into(),
name, UNKNOWN_SPAN
)),
|attr| {
Ok(unsafe {
@ -537,6 +538,7 @@ where
})
},
)?,
UNKNOWN_SPAN,
));
}
@ -547,10 +549,11 @@ where
// Note that whitespace counts as text
XmlEvent::Text(_) => (),
_ => return Err(XmloError::MalformedSymRef(format!(
// This is handled in a better way in the new parser.
_ => panic!(
"preproc:sym-dep must contain only preproc:sym-ref children for `{}`",
name.lookup_str(),
)))
)
}
}

View File

@ -182,6 +182,7 @@ xmlo_tests! {
);
}
// DONE
fn sym_dep_event(sut) {
sut.reader.next_event = Some(Box::new(|_, event_i| match event_i {
0 => Ok(XmlEvent::Start(MockBytesStart::new(
@ -212,14 +213,15 @@ xmlo_tests! {
assert_eq!(
vec![
XmloEvent::SymDepStart("depsym".intern()),
XmloEvent::Symbol("dep1".intern()),
XmloEvent::Symbol("dep2".intern()),
XmloEvent::SymDepStart("depsym".intern(), UNKNOWN_SPAN),
XmloEvent::Symbol("dep1".intern(), UNKNOWN_SPAN),
XmloEvent::Symbol("dep2".intern(), UNKNOWN_SPAN),
],
result
);
}
// DONE
fn sym_dep_fails_with_missing_name(sut) {
sut.reader.next_event = Some(Box::new(|_, _| {
Ok(XmlEvent::Start(MockBytesStart::new(
@ -229,11 +231,12 @@ xmlo_tests! {
}));
match sut.read_event() {
Err(XmloError::UnassociatedSymDep) => (),
Err(XmloError::UnassociatedSymDep(_)) => (),
bad => panic!("expected XmloError: {:?}", bad),
}
}
// DONE
fn sym_dep_malformed_ref_missing_name(sut) {
sut.reader.next_event = Some(Box::new(|_, event_i| match event_i {
0 => Ok(XmlEvent::Start(MockBytesStart::new(
@ -253,46 +256,13 @@ xmlo_tests! {
}));
match sut.read_event() {
Err(XmloError::MalformedSymRef(msg)) => {
assert!(msg.contains("preproc:sym-ref/@name"))
Err(XmloError::MalformedSymRef(name, _)) => {
assert_eq!(name, "depsymbad".into());
},
bad => panic!("expected XmloError: {:?}", bad),
}
}
fn sym_dep_malformed_ref_unexpected_element(sut) {
sut.reader.next_event = Some(Box::new(|_, event_i| match event_i {
0 => Ok(XmlEvent::Start(MockBytesStart::new(
b"preproc:sym-dep",
Some(MockAttributes::new(vec![MockAttribute::new(
b"name", b"depsym-unexpected",
)])),
))),
// text is okay (e.g. whitespace)
1 => Ok(XmlEvent::Text(MockBytesText::new(
b" ",
))),
// unexpected (not a preproc:sym-ref)
2 => Ok(XmlEvent::Empty(MockBytesStart::new(
b"preproc:unexpected",
Some(MockAttributes::new(vec![])),
))),
_ => Err(InnerXmlError::UnexpectedEof(
format!("MockXmlReader out of events: {}", event_i).into(),
)),
}));
match sut.read_event() {
Err(XmloError::MalformedSymRef(msg)) => {
assert!(msg.contains("depsym-unexpected"))
},
bad => panic!("expected XmloError: {:?}", bad),
}
// We should have gotten past the text
assert_eq!(3, sut.reader.event_i, "Did not ignore Text");
}
fn eoh_after_fragments(sut) {
sut.reader.next_event = Some(Box::new(|_, _| {
Ok(XmlEvent::End(MockBytesEnd::new(b"preproc:fragments")))

View File

@ -23,7 +23,7 @@ use super::*;
use crate::{
convert::ExpectInto,
obj::xmlo::{SymDtype, SymType},
parse::{ParseError, ParseState, ParseStatus, Parsed},
parse::{ParseError, ParseState, Parsed},
span::{Span, DUMMY_SPAN},
sym::GlobalSymbolIntern,
xir::{
@ -37,6 +37,7 @@ const S1: Span = DUMMY_SPAN;
const S2: Span = S1.offset_add(1).unwrap();
const S3: Span = S2.offset_add(1).unwrap();
const S4: Span = S3.offset_add(1).unwrap();
const S5: Span = S4.offset_add(1).unwrap();
type Sut = XmloReaderState;
@ -135,70 +136,6 @@ fn ignores_unknown_package_attr() {
);
}
#[test]
fn xmlo_symtable_parser() {
const SSTUB: Span = DUMMY_SPAN.offset_add(50).unwrap();
#[derive(Debug, Default, PartialEq, Eq)]
enum StubSymtableState {
#[default]
None,
}
impl ParseState for StubSymtableState {
type Token = Xirf;
type Object = (SymbolId, SymAttrs, Span);
type Error = XmloError;
fn parse_token(self, tok: Self::Token) -> TransitionResult<Self> {
match tok {
Xirf::Attr(Attr(QN_NAME, name, (s1, s2))) => {
assert_eq!(s1, S1);
assert_eq!(s2, S2);
Transition(Self::None).ok((
name,
SymAttrs::default(),
SSTUB,
))
}
tok => panic!("test expects @name but got {tok:?}"),
}
}
fn is_accepting(&self) -> bool {
*self == Self::None
}
}
let symname = "symname".into();
let attrs = SymAttrs::default();
let toks = [
Xirf::Open(QN_PACKAGE, S1, Depth(0)),
Xirf::Open(QN_SYMTABLE, S2, Depth(1)),
// Our stub parser doesn't need an opening or closing tag.
// Note that S1 and S2 are expected.
Xirf::Attr(Attr(QN_NAME, symname, (S1, S2))), // @name
Xirf::Close(Some(QN_SYMTABLE), S4, Depth(1)),
]
.into_iter();
let sut = XmloReaderState::<StubSymtableState>::parse(toks);
assert_eq!(
Ok(vec![
Parsed::Incomplete, // <package
Parsed::Incomplete, // <preproc:symtable
// SSTUB is used to prove that StubSymtableState was used,
// instead of the SS default (no, not a ship).
Parsed::Object(XmloEvent::SymDecl(symname, attrs, SSTUB)),
Parsed::Incomplete, // </preproc:symtable>
]),
sut.collect(),
);
}
#[test]
fn symtable_err_missing_sym_name() {
let toks = [
@ -505,8 +442,136 @@ fn symtable_map_from_multiple() {
.into_iter();
assert_eq!(
Err(ParseError::StateError(XmloError::MapFromMultiple(name, S3))), // />
Err(ParseError::StateError(XmloError::MapFromMultiple(name, S3))),
SymtableState::parse(toks)
.collect::<Result<Vec<Parsed<<SymtableState as ParseState>::Object>>, _>>(),
);
}
#[test]
fn sym_dep_event() {
let name = "depsym".into();
let dep1 = "dep1".into();
let dep2 = "dep2".into();
let toks = [
Xirf::Open(QN_SYM_DEP, S1, Depth(0)),
Xirf::Attr(Attr(QN_NAME, name, (S2, S3))),
// <preproc:sym-ref
Xirf::Open(QN_SYM_REF, S2, Depth(1)),
Xirf::Attr(Attr(QN_NAME, dep1, (S3, S4))),
Xirf::Close(None, S4, Depth(1)),
// />
// <preproc:sym-ref
Xirf::Open(QN_SYM_REF, S3, Depth(1)),
Xirf::Attr(Attr(QN_NAME, dep2, (S4, S5))),
Xirf::Close(None, S4, Depth(1)),
// />
Xirf::Close(Some(QN_SYM_DEP), S5, Depth(0)),
]
.into_iter();
assert_eq!(
Ok(vec![
Parsed::Incomplete, // <preproc:sym-ref
Parsed::Object(XmloEvent::SymDepStart(name, S1)), // @name
Parsed::Incomplete, // <preproc:sym-ref
Parsed::Object(XmloEvent::Symbol(dep1, S4)), // @name
Parsed::Incomplete, // />
Parsed::Incomplete, // <preproc:sym-ref
Parsed::Object(XmloEvent::Symbol(dep2, S5)), // @name
Parsed::Incomplete, // />
Parsed::Incomplete, // </preproc:sym-dep>
]),
SymDepsState::parse(toks).collect()
);
}
#[test]
fn sym_dep_missing_name() {
let toks = [
Xirf::Open(QN_SYM_DEP, S1, Depth(0)),
// missing @name, causes error
Xirf::Open(QN_SYM_REF, S2, Depth(1)),
]
.into_iter();
assert_eq!(
Err(ParseError::StateError(XmloError::UnassociatedSymDep(S1))),
SymDepsState::parse(toks)
.collect::<Result<Vec<Parsed<<SymDepsState as ParseState>::Object>>, _>>(),
);
}
#[test]
fn sym_ref_missing_name() {
let name = "depsym".into();
let toks = [
Xirf::Open(QN_SYM_DEP, S1, Depth(0)),
Xirf::Attr(Attr(QN_NAME, name, (S2, S3))),
Xirf::Open(QN_SYM_REF, S2, Depth(1)),
// missing @name, causes error
Xirf::Close(None, S3, Depth(1)),
]
.into_iter();
assert_eq!(
Err(ParseError::StateError(XmloError::MalformedSymRef(name, S2))),
SymDepsState::parse(toks)
.collect::<Result<Vec<Parsed<<SymDepsState as ParseState>::Object>>, _>>(),
);
}
/// Very lightly test the default parser composition.
///
/// This test should do just enough to verify that parser state stitching has
/// occurred.
#[test]
fn xmlo_composite_parsers_header() {
let sym_name = "sym".into();
let symdep_name = "symdep".into();
let toks_header = [
Xirf::Open(QN_PACKAGE, S1, Depth(0)),
// <preproc:symtable>
Xirf::Open(QN_SYMTABLE, S2, Depth(1)),
// <preproc:sym
Xirf::Open(QN_SYM, S3, Depth(2)),
Xirf::Attr(Attr(QN_NAME, sym_name, (S2, S3))),
Xirf::Close(None, S4, Depth(2)),
// />
Xirf::Close(Some(QN_SYMTABLE), S4, Depth(1)),
// </preproc:symtable>
// <preproc:sym-deps>
Xirf::Open(QN_SYM_DEPS, S2, Depth(1)),
// <preproc:sym-dep
Xirf::Open(QN_SYM_DEP, S3, Depth(3)),
Xirf::Attr(Attr(QN_NAME, symdep_name, (S2, S3))),
Xirf::Close(Some(QN_SYM_DEP), S4, Depth(3)),
// </preproc:sym-dep>
Xirf::Close(Some(QN_SYM_DEPS), S3, Depth(1)),
// </preproc:sym-deps>
// No closing root node:
// ensure that we can just end at the header without parsing further.
]
.into_iter();
let sut = Sut::parse(toks_header);
assert_eq!(
Ok(vec![
Parsed::Object(XmloEvent::SymDecl(
sym_name,
Default::default(),
S3
)),
Parsed::Object(XmloEvent::SymDepStart(symdep_name, S3)),
]),
sut.filter(|parsed| match parsed {
Ok(Parsed::Incomplete) => false,
_ => true,
})
.collect(),
);
}

View File

@ -81,6 +81,24 @@ pub trait TokenStream<T: Token> = Iterator<Item = T>;
/// consider using [`TokenStream`].
pub trait TokenResultStream<T: Token, E: Error> = Iterator<Item = Result<T, E>>;
/// A [`ParserState`] capable of being automatically stitched together with
/// a parent [`ParserState`] `SP` to create a composite parser.
///
/// Conceptually,
/// this can be visualized as combining the state machines of multiple
/// parsers into one larger state machine.
///
/// The term _state stitching_ refers to a particular pattern able to be
/// performed automatically by this parsing framework;
/// it is not necessary for parser composition,
/// provided that you perform the necessary wiring yourself in absence
/// of state stitching.
pub trait StitchableParseState<SP: ParseState> = ParseState
where
SP: ParseState<Token = <Self as ParseState>::Token>,
<Self as ParseState>::Object: Into<<SP as ParseState>::Object>,
<Self as ParseState>::Error: Into<<SP as ParseState>::Error>;
/// A deterministic parsing automaton.
///
/// These states are utilized by a [`Parser`].
@ -177,13 +195,11 @@ pub trait ParseState: Default + PartialEq + Eq + Debug {
fn delegate<C, SP>(
self,
context: C,
tok: Self::Token,
tok: <Self as ParseState>::Token,
into: impl FnOnce(C, Self) -> SP,
) -> TransitionResult<SP>
where
SP: ParseState<Token = Self::Token>,
Self::Object: Into<<SP as ParseState>::Object>,
Self::Error: Into<<SP as ParseState>::Error>,
Self: StitchableParseState<SP>,
{
use ParseStatus::{Dead, Incomplete, Object as Obj};
@ -210,14 +226,16 @@ pub trait ParseState: Default + PartialEq + Eq + Debug {
fn delegate_lookahead<C, SP>(
self,
context: C,
tok: Self::Token,
tok: <Self as ParseState>::Token,
into: impl FnOnce(C, Self) -> SP,
lookahead: impl FnOnce(C, Self, Self::Token) -> TransitionResult<SP>,
lookahead: impl FnOnce(
C,
Self,
<Self as ParseState>::Token,
) -> TransitionResult<SP>,
) -> TransitionResult<SP>
where
SP: ParseState<Token = Self::Token>,
Self::Object: Into<<SP as ParseState>::Object>,
Self::Error: Into<<SP as ParseState>::Error>,
Self: StitchableParseState<SP>,
{
use ParseStatus::{Dead, Incomplete, Object as Obj};

View File

@ -479,6 +479,9 @@ pub mod st {
L_SRC: cid "src",
L_STATIC: cid "static",
L_SYM: cid "sym",
L_SYM_DEPS: cid "sym-deps",
L_SYM_DEP: cid "sym-dep",
L_SYM_REF: cid "sym-ref",
L_SYMTABLE: cid "symtable",
L_TITLE: cid "title",
L_TPL: cid "tpl",