2023-05-18 15:37:32 -04:00
|
|
|
// ASG IR opaque object parsing
|
|
|
|
//
|
|
|
|
// 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/>.
|
|
|
|
|
|
|
|
//! AIR opaque object parser.
|
|
|
|
//!
|
|
|
|
//! This parser exists primarily to ensure that the parent frame can be held
|
|
|
|
//! on the stack and considered in lexical scoping operations.
|
|
|
|
//!
|
|
|
|
//! See the [parent module](super) for more information.
|
|
|
|
|
|
|
|
use super::{super::AsgError, ir::AirIdent, AirAggregate, AirAggregateCtx};
|
|
|
|
use crate::parse::prelude::*;
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
pub enum AirOpaqueAggregate {
|
|
|
|
Ready,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for AirOpaqueAggregate {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
match self {
|
|
|
|
Self::Ready => {
|
|
|
|
write!(f, "ready for opaque object")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ParseState for AirOpaqueAggregate {
|
|
|
|
type Token = AirIdent;
|
|
|
|
type Object = ();
|
|
|
|
type Error = AsgError;
|
|
|
|
type Context = AirAggregateCtx;
|
|
|
|
type Super = AirAggregate;
|
|
|
|
|
|
|
|
fn parse_token(
|
|
|
|
self,
|
|
|
|
tok: Self::Token,
|
|
|
|
ctx: &mut Self::Context,
|
|
|
|
) -> crate::parse::TransitionResult<Self::Super> {
|
|
|
|
use super::ir::AirIdent::*;
|
|
|
|
use AirOpaqueAggregate::*;
|
|
|
|
|
|
|
|
match (self, tok) {
|
|
|
|
(Ready, IdentDecl(name, kind, src)) => ctx
|
|
|
|
.lookup_lexical_or_missing(name)
|
|
|
|
.declare(ctx.asg_mut(), name, kind, src)
|
|
|
|
.map(|_| ())
|
|
|
|
.transition(Ready),
|
|
|
|
|
|
|
|
(Ready, IdentExternDecl(name, kind, src)) => ctx
|
|
|
|
.lookup_lexical_or_missing(name)
|
|
|
|
.declare_extern(ctx.asg_mut(), name, kind, src)
|
|
|
|
.map(|_| ())
|
|
|
|
.transition(Ready),
|
|
|
|
|
|
|
|
(Ready, IdentDep(name, dep)) => {
|
|
|
|
let oi_from = ctx.lookup_lexical_or_missing(name);
|
|
|
|
let oi_to = ctx.lookup_lexical_or_missing(dep);
|
|
|
|
|
tamer: asg::graph::AsgObjectMut: Allow objects to assert ownership over relationships
There's a lot to say about this; it's been a bit of a struggle figuring out
what I wanted to do here.
First: this allows objects to use `AsgObjectMut` to control whether an edge
is permitted to be added, or to cache information about an edge that is
about to be added. But no object does that yet; it just uses the default
trait implementation, and so this _does not change any current
behavior_. It also is approximately equivalent cycle-count-wise, according
to Valgrind (within ~100 cycles out of hundreds of millions on large package
tests).
Adding edges to the graph is still infallible _after having received
permission_ from an `ObjectIndexRelTo`, but the object is free to reject the
edge with an `AsgError`.
As an example of where this will be useful: the template system needs to
keep track of what is in the body of a template as it is defined. But the
`TplAirAggregate` parser is sidelined while expressions in the body are
parsed, and edges are added to a dynamic source using
`ObjectIndexRelTo`. Consequently, we cannot rely on a static API to cache
information; we have to be able to react dynamically. This will allow `Tpl`
objects to know any time edges are added and, therefore, determine their
shape as the graph is being built, rather than having to traverse the tree
after encountering a close.
(I _could_ change this, but `ObjectIndexRelTo` removes a significant amount
of complexity for the caller, so I'd rather not.)
I did explore other options. I rejected the first one, then rejected this
one, then rejected the first one again before returning back to this one
after having previously sidelined the entire thing, because of the above
example. The core point is: I need confidence that the graph isn't being
changed in ways that I forgot about, and because of the complexity of the
system and the heavy refactoring that I do, I need the compiler's help;
otherwise I risk introducing subtle bugs as objects get out of sync with the
actual state of the graph.
(I wish the graph supported these things directly, but that's a project well
outside the scope of my TAMER work. So I have to make do, as I have been
all this time, by layering atop of Petgraph.)
(...I'm beginning to ramble.)
(...beginning?)
Anyway: my other rejected idea was to provide attestation via the
`ObjectIndex` APIs to force callers to go through those APIs to add an edge
to the graph; it would use sealed objects that are inaccessible to any
modules other than the objects, and assert that the caller is able to
provide a zero-sized object of that sealed type.
The problem with this is...exactly what was mentioned above:
`ObjectIndexRelTo` is dynamic. We don't always know the source object type
statically, and so we cannot make those static assertions.
I could have tried the same tricks to store attestation at some other time,
but what a confusing mess it would be.
And so here we are.
Most of this work is cleaning up the callers---adding edges is now fallible,
from the `ObjectIndex` API standpoint, and so AIR needed to be set up to
handle those failures. There _aren't_ any failures yet, but again, since
things are dynamic, they could appear at any moment. Furthermore, since
ref/def is commutative (things can be defined and referenced in any order),
there could be surprise errors on edge additions in places that might not
otherwise expect it in the future. We're now ready for that, and I'll be
able to e.g. traverse incoming edges on a `Missing->Transparent` definition
to notify dependents.
This project is going to be the end of me. As interesting as it is.
I can see why Rust just chose to require macro definitions _before_ use. So
much less work.
DEV-13163
2023-07-24 16:41:32 -04:00
|
|
|
oi_from
|
|
|
|
.add_opaque_dep(ctx.asg_mut(), oi_to)
|
|
|
|
.map(|_| ())
|
|
|
|
.transition(Ready)
|
2023-05-18 15:37:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
(Ready, IdentFragment(name, text)) => ctx
|
|
|
|
.lookup_lexical_or_missing(name)
|
|
|
|
.set_fragment(ctx.asg_mut(), text)
|
|
|
|
.map(|_| ())
|
|
|
|
.transition(Ready),
|
|
|
|
|
tamer: asg::graph::AsgObjectMut: Allow objects to assert ownership over relationships
There's a lot to say about this; it's been a bit of a struggle figuring out
what I wanted to do here.
First: this allows objects to use `AsgObjectMut` to control whether an edge
is permitted to be added, or to cache information about an edge that is
about to be added. But no object does that yet; it just uses the default
trait implementation, and so this _does not change any current
behavior_. It also is approximately equivalent cycle-count-wise, according
to Valgrind (within ~100 cycles out of hundreds of millions on large package
tests).
Adding edges to the graph is still infallible _after having received
permission_ from an `ObjectIndexRelTo`, but the object is free to reject the
edge with an `AsgError`.
As an example of where this will be useful: the template system needs to
keep track of what is in the body of a template as it is defined. But the
`TplAirAggregate` parser is sidelined while expressions in the body are
parsed, and edges are added to a dynamic source using
`ObjectIndexRelTo`. Consequently, we cannot rely on a static API to cache
information; we have to be able to react dynamically. This will allow `Tpl`
objects to know any time edges are added and, therefore, determine their
shape as the graph is being built, rather than having to traverse the tree
after encountering a close.
(I _could_ change this, but `ObjectIndexRelTo` removes a significant amount
of complexity for the caller, so I'd rather not.)
I did explore other options. I rejected the first one, then rejected this
one, then rejected the first one again before returning back to this one
after having previously sidelined the entire thing, because of the above
example. The core point is: I need confidence that the graph isn't being
changed in ways that I forgot about, and because of the complexity of the
system and the heavy refactoring that I do, I need the compiler's help;
otherwise I risk introducing subtle bugs as objects get out of sync with the
actual state of the graph.
(I wish the graph supported these things directly, but that's a project well
outside the scope of my TAMER work. So I have to make do, as I have been
all this time, by layering atop of Petgraph.)
(...I'm beginning to ramble.)
(...beginning?)
Anyway: my other rejected idea was to provide attestation via the
`ObjectIndex` APIs to force callers to go through those APIs to add an edge
to the graph; it would use sealed objects that are inaccessible to any
modules other than the objects, and assert that the caller is able to
provide a zero-sized object of that sealed type.
The problem with this is...exactly what was mentioned above:
`ObjectIndexRelTo` is dynamic. We don't always know the source object type
statically, and so we cannot make those static assertions.
I could have tried the same tricks to store attestation at some other time,
but what a confusing mess it would be.
And so here we are.
Most of this work is cleaning up the callers---adding edges is now fallible,
from the `ObjectIndex` API standpoint, and so AIR needed to be set up to
handle those failures. There _aren't_ any failures yet, but again, since
things are dynamic, they could appear at any moment. Furthermore, since
ref/def is commutative (things can be defined and referenced in any order),
there could be surprise errors on edge additions in places that might not
otherwise expect it in the future. We're now ready for that, and I'll be
able to e.g. traverse incoming edges on a `Missing->Transparent` definition
to notify dependents.
This project is going to be the end of me. As interesting as it is.
I can see why Rust just chose to require macro definitions _before_ use. So
much less work.
DEV-13163
2023-07-24 16:41:32 -04:00
|
|
|
(Ready, IdentRoot(name)) => ctx
|
|
|
|
.lookup_lexical_or_missing(name)
|
|
|
|
.root(ctx.asg_mut())
|
|
|
|
.map(|_| ())
|
|
|
|
.transition(Ready),
|
2023-05-18 15:37:32 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_accepting(&self, _: &Self::Context) -> bool {
|
|
|
|
matches!(self, Self::Ready)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AirOpaqueAggregate {
|
|
|
|
pub(super) fn new() -> Self {
|
|
|
|
Self::Ready
|
|
|
|
}
|
|
|
|
}
|