2023-01-30 16:51:24 -05:00
|
|
|
// Packages represented on ASG
|
|
|
|
//
|
|
|
|
// 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/>.
|
|
|
|
|
|
|
|
//! Package object on the ASG.
|
|
|
|
|
2023-04-20 14:55:20 -04:00
|
|
|
use super::{prelude::*, Doc, Ident, NameableMissingObject, Tpl};
|
2023-04-06 22:49:13 -04:00
|
|
|
use crate::{
|
|
|
|
f::Functor,
|
2023-05-02 15:21:03 -04:00
|
|
|
fmt::{DisplayWrapper, TtQuote},
|
2023-04-06 22:49:13 -04:00
|
|
|
parse::{util::SPair, Token},
|
|
|
|
span::Span,
|
2023-01-31 16:37:25 -05:00
|
|
|
};
|
2023-02-03 15:53:50 -05:00
|
|
|
use std::fmt::Display;
|
|
|
|
|
|
|
|
#[cfg(doc)]
|
|
|
|
use super::ObjectKind;
|
2023-01-30 16:51:24 -05:00
|
|
|
|
tamer: src::asg::graph::object::pkg::name: New module
This introduces, but does not yet integrate, `CanonicalName`, which not only
represents canonicalized package names, but handles namespec resolution.
The term "namespec" is motivated by Git's use of *spec (e.g. refspec)
referring to various ways of specifying a particular object. Names look
like paths, and are derived from them, but they _are not paths_. Their
resolution is a purely lexical operation, and they include a number of
restrictions to simplify their clarity and handling. I expect them to
evolve more in the future, and I've had ideas to do so for quite some time.
In particular, resolving packages in this way and then loading the from the
filesystem relative to the project root will ensure that
traversing (conceptually) to a parent directory will not operate
unintuitively with symlinks. The path will always resolve unambigiously.
(With that said, if the symlink is to a shared directory with different
directory structures, that doesn't solve the compilation problem---we'll
have to move object files into a project-specific build directory to handle
that.)
Span Slicing
------------
Okay, it's worth commenting on the horridity of the path name slicing that
goes on here. Care has been taken to ensure that spans will be able to be
properly sliced in all relevant contexts, and there are plenty of words
devoted to that in the documentation committed here.
But there is a more fundamental problem here that I regret not having solved
earlier, because I don't have the time for it right now: while we do have
SPair, it makes no guarantees that the span associated with the corresponding
SymbolId is actually the span that matches the original source lexeme. In
fact, it's often not.
This is a problem when we want to slice up a symbol in an SPair and produce
a sensible span. If it _is_ a source lexeme with its original span, that's
no problem. But if it's _not_, then the two are not in sync, and slicing up
the span won't produce something that actually makes sense to the user. Or,
worse (or maybe it's not worse?), it may cause a panic if the slicing is out
of bounds.
The solution in the future might be to store explicitly the state of an
SPair, or call it Lexeme, or something, so that we know the conditions under
which slicing is safe. If I ever have time for that in this project.
But the result of the lack of a proper abstraction really shows here: this
is some of the most confusing code in TAMER, and it's really not doing
anything all that complicated. It is disproportionately confusing.
DEV-13162
2023-05-04 12:28:08 -04:00
|
|
|
mod name;
|
|
|
|
|
2023-01-30 16:51:24 -05:00
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
2023-04-06 22:49:13 -04:00
|
|
|
pub struct Pkg(Span, PathSpec);
|
|
|
|
|
2023-01-30 16:51:24 -05:00
|
|
|
impl Pkg {
|
2023-05-02 22:12:37 -04:00
|
|
|
/// Create a new package with a canonicalized name.
|
|
|
|
///
|
|
|
|
/// A canonical package name is a path relative to the project root.
|
|
|
|
pub(super) fn new_canonical<S: Into<Span>>(start: S, name: SPair) -> Self {
|
|
|
|
Self(start.into(), PathSpec::Canonical(name))
|
2023-04-06 22:49:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Represent a package imported according to the provided
|
|
|
|
/// [`PathSpec`].
|
2023-05-02 15:21:03 -04:00
|
|
|
pub fn new_imported(pathspec: SPair) -> Self {
|
|
|
|
Self(pathspec.span(), PathSpec::TodoImport(pathspec))
|
2023-01-30 16:51:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn span(&self) -> Span {
|
|
|
|
match self {
|
2023-04-06 22:49:13 -04:00
|
|
|
Self(span, _) => *span,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-02 15:21:03 -04:00
|
|
|
/// The canonical name for this package assigned by
|
2023-05-02 22:12:37 -04:00
|
|
|
/// [`Self::new_canonical`],
|
2023-05-02 15:21:03 -04:00
|
|
|
/// if any.
|
|
|
|
pub fn canonical_name(&self) -> Option<SPair> {
|
|
|
|
match self {
|
|
|
|
Self(_, pathspec) => pathspec.canonical_name(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The import path to this package as provided by
|
|
|
|
/// [`Self::new_imported`],
|
|
|
|
/// if any.
|
|
|
|
pub fn import_path(&self) -> Option<SPair> {
|
2023-04-06 22:49:13 -04:00
|
|
|
match self {
|
2023-05-02 15:21:03 -04:00
|
|
|
Self(_, pathspec) => pathspec.import_path(),
|
2023-01-30 16:51:24 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for Pkg {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
write!(f, "package")
|
|
|
|
}
|
|
|
|
}
|
2023-01-31 16:37:25 -05:00
|
|
|
|
2023-04-20 14:55:20 -04:00
|
|
|
impl NameableMissingObject for Pkg {
|
|
|
|
fn missing(pathspec: SPair) -> Self {
|
|
|
|
Self::new_imported(pathspec)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-07 12:19:27 -05:00
|
|
|
impl Functor<Span> for Pkg {
|
|
|
|
fn map(self, f: impl FnOnce(Span) -> Span) -> Self::Target {
|
|
|
|
match self {
|
2023-04-06 22:49:13 -04:00
|
|
|
Self(span, path) => Self(f(span), path),
|
2023-02-07 12:19:27 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-02 15:21:03 -04:00
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
|
|
enum PathSpec {
|
|
|
|
/// Canonical package name.
|
|
|
|
///
|
|
|
|
/// This is the name of the package relative to the project root.
|
|
|
|
/// This is like the module name after `crate::` in Rust,
|
|
|
|
/// but with `/` package separators in place of `::`.
|
|
|
|
Canonical(SPair),
|
|
|
|
|
|
|
|
/// Import path relative to the current package
|
|
|
|
/// (which is likely the compilation unit).
|
|
|
|
///
|
|
|
|
/// TODO: This will be replaced with [`Self::Canonical`] once that is
|
|
|
|
/// working and relative paths can be resolved against the active
|
|
|
|
/// package.
|
|
|
|
TodoImport(SPair),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PathSpec {
|
|
|
|
fn canonical_name(&self) -> Option<SPair> {
|
|
|
|
use PathSpec::*;
|
|
|
|
|
|
|
|
match self {
|
|
|
|
Canonical(spair) => Some(*spair),
|
|
|
|
TodoImport(_) => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn import_path(&self) -> Option<SPair> {
|
|
|
|
use PathSpec::*;
|
|
|
|
|
|
|
|
match self {
|
|
|
|
// TODO: Let's wait to see if we actually need this,
|
|
|
|
// since we'll need to allocate and intern a `/`-prefixed
|
|
|
|
// symbol.
|
|
|
|
Canonical(_) => None,
|
|
|
|
TodoImport(spair) => Some(*spair),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for PathSpec {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
use PathSpec::*;
|
|
|
|
|
|
|
|
match self {
|
|
|
|
Canonical(spair) => write!(f, "package {}", TtQuote::wrap(spair)),
|
|
|
|
TodoImport(spair) => write!(
|
|
|
|
f,
|
|
|
|
"package import {} relative to compilation unit",
|
|
|
|
TtQuote::wrap(spair)
|
|
|
|
),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-25 23:56:05 -05:00
|
|
|
object_rel! {
|
2023-02-03 15:53:50 -05:00
|
|
|
/// Packages serve as a root for all identifiers defined therein,
|
|
|
|
/// and so an edge to [`Ident`] will never be a cross edge.
|
|
|
|
///
|
|
|
|
/// Imported [`Ident`]s do not have edges from this package.
|
2023-02-25 23:56:05 -05:00
|
|
|
Pkg -> {
|
2023-04-06 22:49:13 -04:00
|
|
|
// Package import
|
|
|
|
cross Pkg,
|
|
|
|
|
|
|
|
// Identified objects owned by this package.
|
2023-02-25 23:56:05 -05:00
|
|
|
tree Ident,
|
tamer: Very basic support for template application NIR -> xmli
This this a big change that's difficult to break up, and I don't have the
energy after it.
This introduces nullary template application, short- and long-form. Note
that a body of the short form is a `@values@` argument, so that's not
supported yet.
This continues to formalize the idea of what "template application" and
"template expansion" mean in TAMER. It makes a separate `TplApply`
unnecessary, because now application is simply a reference to a
template. Expansion and application are one and the same: when a template
expands, it'll re-bind metavariables to the parent context. So in a
template context, this amounts to application.
But applying a closed template will have nothing to bind, and so is
equivalent to expansion. And since `Meta` objects are not valid outside of
a `Tpl` context, applying a non-closed template outside of another template
will be invalid.
So we get all of this with a single primitive (getting the "value" of a
template).
The expansion is conceptually like `,@` in Lisp, where we're splicing trees.
It's a mess in some spots, but I want to get this committed before I do a
little bit of cleanup.
2023-03-17 10:25:56 -04:00
|
|
|
|
|
|
|
// Anonymous templates are used for expansion.
|
|
|
|
tree Tpl,
|
2023-04-12 10:03:37 -04:00
|
|
|
|
|
|
|
// Arbitrary blocks of text serving as documentation.
|
|
|
|
tree Doc,
|
2023-01-31 16:37:25 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ObjectIndex<Pkg> {
|
2023-02-07 12:19:27 -05:00
|
|
|
/// Complete the definition of a package.
|
|
|
|
pub fn close(self, asg: &mut Asg, span: Span) -> Self {
|
|
|
|
self.map_obj(asg, Pkg::fmap(|open| open.merge(span).unwrap_or(open)))
|
|
|
|
}
|
2023-04-06 22:49:13 -04:00
|
|
|
|
|
|
|
/// Indicate that a package should be imported at the provided
|
|
|
|
/// pathspec.
|
|
|
|
///
|
|
|
|
/// This simply adds the import to the graph;
|
|
|
|
/// package loading must be performed by another subsystem.
|
|
|
|
pub fn import(self, asg: &mut Asg, pathspec: SPair) -> Self {
|
|
|
|
let oi_import = asg.create(Pkg::new_imported(pathspec));
|
|
|
|
self.add_edge_to(asg, oi_import, Some(pathspec.span()))
|
|
|
|
}
|
2023-04-12 10:03:37 -04:00
|
|
|
|
|
|
|
/// Arbitrary text serving as documentation in a literate style.
|
|
|
|
pub fn append_doc_text(&self, asg: &mut Asg, text: SPair) -> Self {
|
|
|
|
let oi_doc = asg.create(Doc::new_text(text));
|
|
|
|
self.add_edge_to(asg, oi_doc, None)
|
|
|
|
}
|
2023-01-31 16:37:25 -05:00
|
|
|
}
|