tame/tamer/src/asg/mod.rs

92 lines
3.2 KiB
Rust
Raw Normal View History

// Abstract semantic graph (ASG) intermediate representation (IR)
//
// Copyright (C) 2014-2023 Ryan Specialty, LLC.
2020-03-06 11:05:18 -05:00
//
// 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/>.
//! Abstract semantic graph.
//!
//! The [abstract semantic graph][asg] (ASG) is an IR representing the
//! relationship between objects using a directed [graph][].
//! An _object_ is an identifier or expression.
//!
//! Since TAME is a declarative language,
//! the ASG does not represent control flow;
//! instead, it represents the relationship between objects and their
//! dependencies.
//! Control flow is determined solely by the [linker][crate::ld] based on
//! these dependencies.
//!
//! See [`crate::global`] for available index sizes depending on context.
//! For example,
//! a linker may choose to use [`crate::global::ProgIdentSize`];
//!
//!
//! Graph Ontology
//! ==============
tamer: asg::graph: Static- and runtime-enforced multi-kind edge ontolgoy This allows for edges to be multiple types, and gives us two important benefits: (a) Compiler-verified correctness to ensure that we don't generate graphs that do not adhere to the ontology; and (b) Runtime verification of types, so that bugs are still memory safe. There is a lot more information in the documentation within the patch. This took a lot of iterating to get something that was tolerable. There's quite a bit of boilerplate here, and maybe that'll be abstracted away better in the future as the graph grows. In particular, it was challenging to determine how I wanted to actually go about narrowing and looking up edges. Initially I had hoped to represent the subsets as `ObjectKind`s as well so that you could use them anywhere `ObjectKind` was expected, but that proved to be far too difficult because I cannot return a reference to a subset of `Object` (the value would be owned on generation). And while in a language like C maybe I'd pad structures and cast between them safely, since they _do_ overlap, I can't confidently do that here since Rust's discriminant and layout are not under my control. I tried playing around with `std::mem::Discriminant` as well, but `discriminant` (the function) requires a _value_, meaning I couldn't get the discriminant of a static `Object` variant without some dummy value; wasn't worth it over `ObjectRelTy.` We further can't assign values to enum variants unless they hold no data. Rust a decade from now may be different and will be interesting to look back on this struggle. DEV-13597
2023-01-23 11:40:10 -05:00
//! Each node (vertex) in the graph represents an [`Object`],
//! such as an identifier or an expression.
tamer: asg::graph: Static- and runtime-enforced multi-kind edge ontolgoy This allows for edges to be multiple types, and gives us two important benefits: (a) Compiler-verified correctness to ensure that we don't generate graphs that do not adhere to the ontology; and (b) Runtime verification of types, so that bugs are still memory safe. There is a lot more information in the documentation within the patch. This took a lot of iterating to get something that was tolerable. There's quite a bit of boilerplate here, and maybe that'll be abstracted away better in the future as the graph grows. In particular, it was challenging to determine how I wanted to actually go about narrowing and looking up edges. Initially I had hoped to represent the subsets as `ObjectKind`s as well so that you could use them anywhere `ObjectKind` was expected, but that proved to be far too difficult because I cannot return a reference to a subset of `Object` (the value would be owned on generation). And while in a language like C maybe I'd pad structures and cast between them safely, since they _do_ overlap, I can't confidently do that here since Rust's discriminant and layout are not under my control. I tried playing around with `std::mem::Discriminant` as well, but `discriminant` (the function) requires a _value_, meaning I couldn't get the discriminant of a static `Object` variant without some dummy value; wasn't worth it over `ObjectRelTy.` We further can't assign values to enum variants unless they hold no data. Rust a decade from now may be different and will be interesting to look back on this struggle. DEV-13597
2023-01-23 11:40:10 -05:00
//! For information on how [`Object`]s are stored and represented on the
//! graph,
//! and for information on relationships between objects,
//! see the [`graph::object`] module.
//!
//! A visualization of the graph ontology is provided here:
//!
//! ![Visualization of ASG ontology](./ontviz.svg)
//!
//! Graphs may contain cycles for recursive functions—that is,
//! TAME's ASG is _not_ a DAG.
//! Mutually recursive functions are therefore represented as
//! [strongly connected components][scc].
//!
//! [asg]: https://en.wikipedia.org/wiki/Abstract_semantic_graph
//! [graph]: https://en.wikipedia.org/wiki/Graph_(discrete_mathematics)
//! [scc]: https://en.wikipedia.org/wiki/Strongly_connected_component
//!
//! Missing Identifiers
//! -------------------
//! Since identifiers in TAME can be defined in any order relative to their
//! dependencies within a source file,
//! it is often the case that a dependency will have to be added to the
//! graph before it is resolved.
//! For example,
//! [`Asg::add_dep_lookup`] will add an [`Ident::Missing`] to the graph
//! if either identifier has not yet been declared.
mod error;
mod graph;
tamer: Refactor asg_builder into obj::xmlo::lower and asg::air This finally uses `parse` all the way up to aggregation into the ASG, as can be seen by the mess in `poc`. This will be further simplified---I just need to get this committed so that I can mentally get it off my plate. I've been separating this commit into smaller commits, but there's a point where it's just not worth the effort anymore. I don't like making large changes such as this one. There is still work to do here. First, it's worth re-mentioning that `poc` means "proof-of-concept", and represents things that still need a proper home/abstraction. Secondly, `poc` is retrieving the context of two parsers---`LowerContext` and `Asg`. The latter is desirable, since it's the final aggregation point, but the former needs to be eliminated; in particular, packages need to be worked into the ASG so that `found` can be removed. Recursively loading `xmlo` files still happens in `poc`, but the compiler will need this as well. Once packages are on the ASG, along with their state, that responsibility can be generalized as well. That will then simplify lowering even further, to the point where hopefully everything has the same shape (once final aggregation has an abstraction), after which we can then create a final abstraction to concisely stitch everything together. Right now, Rust isn't able to infer `S` for `Lower<S, LS>`, which is unfortunate, but we'll be able to help it along with a more explicit abstraction. DEV-11864
2022-05-27 13:51:29 -04:00
pub mod air;
pub use error::AsgError;
pub use graph::{
object::{
expr::{Expr, ExprDim, ExprOp},
ident::{
FragmentText, Ident, IdentKind, Source, TransitionError,
TransitionResult, UnresolvedError,
},
Object, ObjectIndex, ObjectKind,
},
visit,
xmli::AsgTreeToXirf,
Asg, AsgResult, IndexType,
};
/// Default concrete ASG implementation.
pub type DefaultAsg = graph::Asg;