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-13708
main
Mike Gerwitz 2023-02-21 23:58:58 -05:00
parent 33d2b4f0b8
commit f8c1ef5ef2
6 changed files with 159 additions and 32 deletions

View File

@ -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;

View File

@ -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
}
}

View File

@ -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;

View File

@ -76,6 +76,7 @@ pub use graph::{
FragmentText, Ident, IdentKind, Source, TransitionError,
TransitionResult, UnresolvedError,
},
xir::AsgTreeToXirf,
Object, ObjectIndex, ObjectKind,
},
visit, Asg, AsgResult, IndexType,

View File

@ -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(())
}

View File

@ -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)),
})
}