tamer: tamec: MILESONE: POC end-to-end lowering
This has been a long time coming. The wiring of it all together is a little rough around the edges right now, but this commit represents a working POC to begin to fill in the gaps for the entire lowering pipeline. I had hoped to be at this point a year ago. Yeah. This marks a significant milestone in the project because this allows me to begin to observe the implementation end-to-end, testing it on real-life inputs as part of a production build pipeline. ...and now, with that, we can begin. So much work has gone into this project so far, but aside from the linker (which has been in production for years), most of this work has been foundational. It's been a significant investment that I intend to have pay off in many different ways. (All this outputs right now is `<package/>`.) DEV-13708main
parent
33d2b4f0b8
commit
f8c1ef5ef2
|
@ -122,6 +122,7 @@ pub mod ident;
|
|||
pub mod pkg;
|
||||
mod rel;
|
||||
pub mod root;
|
||||
pub mod xir;
|
||||
|
||||
pub use expr::Expr;
|
||||
pub use ident::Ident;
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
// XML representation of graph objects
|
||||
//
|
||||
// Copyright (C) 2014-2023 Ryan Specialty, LLC.
|
||||
//
|
||||
// This file is part of TAME.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! XML representation of graph objects via [XIR](crate::xir).
|
||||
//!
|
||||
//! Attempts to produce a representation of graph [`Object`]s that is
|
||||
//! familiar to those writing TAME's XML-based source language.
|
||||
//!
|
||||
//! _This representation will change over time_ as TAME's source language
|
||||
//! evolves.
|
||||
//! There is no guarantee that this representation will stay over time;
|
||||
//! it was written for transitional purposes,
|
||||
//! 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 crate::{
|
||||
asg::{visit::TreeWalkRel, Asg},
|
||||
diagnose::Annotate,
|
||||
diagnostic_unreachable,
|
||||
parse::prelude::*,
|
||||
xir::{
|
||||
flat::{Text, XirfToken},
|
||||
st::qname::QN_PACKAGE,
|
||||
OpenSpan,
|
||||
},
|
||||
};
|
||||
|
||||
use super::ObjectRelTy;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum AsgTreeToXirf<'a> {
|
||||
Ready(PhantomData<&'a ()>),
|
||||
}
|
||||
|
||||
impl<'a> Default for AsgTreeToXirf<'a> {
|
||||
fn default() -> Self {
|
||||
Self::Ready(PhantomData::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for AsgTreeToXirf<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "generating XIRF sources from ASG tree")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ParseState for AsgTreeToXirf<'a> {
|
||||
type Token = TreeWalkRel;
|
||||
type Object = XirfToken<Text>;
|
||||
type Error = Infallible;
|
||||
|
||||
/// Read-only reference to the [`Asg`].
|
||||
///
|
||||
/// This creates an awkward `&mut &'a Asg` in the signature of
|
||||
/// [`Self::parse_token`],
|
||||
/// but Rust's auto-deref makes use of it transparent.
|
||||
/// The use of the context in this way is a bit of a kluge that maybe
|
||||
/// will be improved upon in the future.
|
||||
type Context = &'a Asg;
|
||||
|
||||
fn parse_token(
|
||||
self,
|
||||
tok: Self::Token,
|
||||
asg: &mut &'a Asg,
|
||||
) -> TransitionResult<Self::Super> {
|
||||
use ObjectRelTy as Ty;
|
||||
|
||||
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::Ident | Ty::Expr => Transition(self).incomplete(),
|
||||
|
||||
Ty::Root => diagnostic_unreachable!(
|
||||
vec![tok_span.error("unexpected Root")],
|
||||
"tree walk is not expected to emit Root",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_accepting(&self, _ctx: &Self::Context) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
|
@ -27,7 +27,7 @@ use std::fmt::Display;
|
|||
|
||||
use super::{object::DynObjectRel, Asg, Object, ObjectIndex};
|
||||
use crate::{
|
||||
parse::Token,
|
||||
parse::{self, Token},
|
||||
span::{Span, UNKNOWN_SPAN},
|
||||
};
|
||||
|
||||
|
@ -270,5 +270,7 @@ impl Token for TreeWalkRel {
|
|||
}
|
||||
}
|
||||
|
||||
impl parse::Object for TreeWalkRel {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
|
|
@ -76,6 +76,7 @@ pub use graph::{
|
|||
FragmentText, Ident, IdentKind, Source, TransitionError,
|
||||
TransitionResult, UnresolvedError,
|
||||
},
|
||||
xir::AsgTreeToXirf,
|
||||
Object, ObjectIndex, ObjectKind,
|
||||
},
|
||||
visit, Asg, AsgResult, IndexType,
|
||||
|
|
|
@ -215,53 +215,64 @@ fn compile<R: Reporter>(
|
|||
/// TAMER reasons about the system using a different paradigm.
|
||||
#[cfg(feature = "wip-asg-derived-xmli")]
|
||||
fn derive_xmli(
|
||||
_asg: tamer::asg::Asg,
|
||||
asg: tamer::asg::Asg,
|
||||
mut fout: impl std::io::Write,
|
||||
escaper: &DefaultEscaper,
|
||||
) -> Result<(), UnrecoverableError> {
|
||||
use tamer::{
|
||||
convert::ExpectInto,
|
||||
asg::{
|
||||
visit::{tree_reconstruction, TreeWalkRel},
|
||||
AsgTreeToXirf,
|
||||
},
|
||||
iter::TrippableIterator,
|
||||
parse::terminal,
|
||||
span::UNKNOWN_SPAN,
|
||||
xir::{
|
||||
autoclose::XirfAutoClose,
|
||||
flat::{Depth, Text, XirfToXir},
|
||||
flat::{Text, XirfToXir},
|
||||
writer::XmlWriter,
|
||||
OpenSpan,
|
||||
},
|
||||
};
|
||||
|
||||
// Note how this does not contain a closing token;
|
||||
// it is closed by `XirfAutoClose`.
|
||||
let xir_toks = vec![XirfToken::Open(
|
||||
"it-has-begun".unwrap_into(),
|
||||
OpenSpan::without_name_span(UNKNOWN_SPAN),
|
||||
Depth(0),
|
||||
)];
|
||||
|
||||
let mut head = lowerable(xir_toks.into_iter().map(Ok));
|
||||
let mut head =
|
||||
lowerable::<UnknownToken, _, _>(tree_reconstruction(&asg).map(Ok))
|
||||
.map(|result| result.map_err(UnrecoverableError::from));
|
||||
|
||||
// THIS IS A PROOF-OF-CONCEPT LOWERING PIPELINE.
|
||||
Lower::<
|
||||
ParsedObject<XirfToken<Text>, XirfToken<Text>, Infallible>,
|
||||
XirfAutoClose,
|
||||
ParsedObject<UnknownToken, TreeWalkRel, Infallible>,
|
||||
AsgTreeToXirf,
|
||||
_,
|
||||
>::lower::<_, UnrecoverableError>(&mut head, |xirf| {
|
||||
Lower::<XirfAutoClose, XirfToXir<Text>, _>::lower(xirf, |xir| {
|
||||
terminal::<XirfToXir<Text>, UnrecoverableError>(xir).while_ok(
|
||||
|toks| {
|
||||
// Write failures should immediately bail out;
|
||||
// we can't skip writing portions of the file and
|
||||
// just keep going!
|
||||
toks.write(&mut fout, Default::default(), escaper)?;
|
||||
Ok::<_, UnrecoverableError>(())
|
||||
>::lower_with_context::<_, UnrecoverableError>(
|
||||
&mut head,
|
||||
&asg,
|
||||
|xirf_unclosed| {
|
||||
Lower::<AsgTreeToXirf, XirfAutoClose, _>::lower(
|
||||
xirf_unclosed,
|
||||
|xirf| {
|
||||
Lower::<XirfAutoClose, XirfToXir<Text>, _>::lower(
|
||||
xirf,
|
||||
|xir| {
|
||||
terminal::<XirfToXir<Text>, _>(xir).while_ok(
|
||||
|toks| {
|
||||
// Write failures should immediately bail out;
|
||||
// we can't skip writing portions of the file and
|
||||
// just keep going!
|
||||
toks.write(
|
||||
&mut fout,
|
||||
Default::default(),
|
||||
escaper,
|
||||
)?;
|
||||
Ok::<_, UnrecoverableError>(())
|
||||
},
|
||||
// TODO: Remove bad file?
|
||||
// Let make do it?
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
// TODO: Remove bad file?
|
||||
// Let make do it?
|
||||
)
|
||||
})
|
||||
})?;
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -280,12 +280,12 @@ pub fn terminal<
|
|||
S: ParseState,
|
||||
E: Diagnostic + From<ParseError<S::Token, S::Error>>,
|
||||
>(
|
||||
iter: impl Iterator<Item = ParsedResult<S>>,
|
||||
iter: impl Iterator<Item = Result<Parsed<S::Object>, E>>,
|
||||
) -> impl Iterator<Item = Result<S::Object, E>> {
|
||||
iter.filter_map(|result| match result {
|
||||
Ok(Parsed::Incomplete) => None,
|
||||
Ok(Parsed::Object(obj)) => Some(Ok(obj)),
|
||||
Err(e) => Some(Err(e.into())),
|
||||
Err(e) => Some(Err(e)),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue