tamer: asg::air: Include package in opaque identifier scope

This was the remaining of my stashed changes that I had mentioned in a
previous commit, but is accomplished differently than I had prototyped.  My
initial approach was a bit too klugey: to accept as an argument in various
scope contexts the active parser, as if it were the top stack frame.  This
was prototyped before the `AirPkgAggregate` parser was even created.

So we've since created a Pkg parser and now an opaque parser for opaque
idents.  There may be other opaque objects in the future.

Because of this change, the parent `AirPkgAggregate` gets stored on the
stack and just naturally becomes part of the lexical scope determination,
and so everything Just Works!

This commit was _supposed_ to be moving the index from `Asg` onto
`AirAggregateCtx`, but I wasn't able to do that because that context is
re-created for each package import currently.

DEV-13162
main
Mike Gerwitz 2023-05-18 15:37:32 -04:00
parent e266d42c48
commit 92214c7e05
5 changed files with 197 additions and 48 deletions

View File

@ -40,6 +40,7 @@ use super::{
Asg, AsgError, Expr, Ident, ObjectIndex,
};
use crate::{
diagnose::Annotate,
f::Functor,
parse::{prelude::*, StateStack},
span::Span,
@ -55,9 +56,11 @@ mod ir;
pub use ir::Air;
mod expr;
mod opaque;
mod pkg;
mod tpl;
use expr::AirExprAggregate;
use opaque::AirOpaqueAggregate;
use pkg::AirPkgAggregate;
use tpl::AirTplAggregate;
@ -94,6 +97,12 @@ pub enum AirAggregate {
/// parented to this template rather than the parent [`Pkg`].
/// See [`Air::TplStart`] for more information.
PkgTpl(AirTplAggregate),
/// Parsing opaque objects.
///
/// This parser is intended for loading declarations from object files
/// without loading their corresponding definitions.
PkgOpaque(AirOpaqueAggregate),
}
impl Display for AirAggregate {
@ -110,7 +119,10 @@ impl Display for AirAggregate {
write!(f, "defining a package expression: {expr}")
}
PkgTpl(tpl) => {
write!(f, "building a template: {tpl}",)
write!(f, "building a template: {tpl}")
}
PkgOpaque(opaque) => {
write!(f, "loading opaque objects: {opaque}")
}
}
}
@ -134,6 +146,12 @@ impl From<AirTplAggregate> for AirAggregate {
}
}
impl From<AirOpaqueAggregate> for AirAggregate {
fn from(st: AirOpaqueAggregate) -> Self {
Self::PkgOpaque(st)
}
}
impl ParseState for AirAggregate {
type Token = Air;
type Object = ();
@ -164,12 +182,18 @@ impl ParseState for AirAggregate {
(st, AirTodo(Todo(_))) => Transition(st).incomplete(),
// Package
(st @ (Root(..) | PkgExpr(..) | PkgTpl(..)), tok @ AirPkg(..)) => {
ctx.ret_or_transfer(st, tok, AirPkgAggregate::new())
}
//
// Note that `ret_or_transfer` will return from the active frame
// if it is in an accepting state,
// and so encountering a properly nested `PkgClose` will pop
// frames off of the stack until reaching the still-active
// parent package frame.
(
st @ (Root(..) | PkgExpr(..) | PkgTpl(..) | PkgOpaque(..)),
tok @ AirPkg(..),
) => ctx.ret_or_transfer(st, tok, AirPkgAggregate::new()),
(Pkg(pkg), AirPkg(etok)) => ctx.proxy(pkg, etok),
(Pkg(pkg), AirBind(etok)) => ctx.proxy(pkg, etok),
(Pkg(pkg), AirIdent(etok)) => ctx.proxy(pkg, etok),
(Pkg(pkg), AirDoc(etok)) => ctx.proxy(pkg, etok),
// Expression
@ -188,6 +212,36 @@ impl ParseState for AirAggregate {
(PkgTpl(tplst), AirBind(ttok)) => ctx.proxy(tplst, ttok),
(PkgTpl(tplst), AirDoc(ttok)) => ctx.proxy(tplst, ttok),
// Opaque
//
// By having opaque object loading be its _own_ child parser,
// we ensure that the active package frame becomes held on the
// stack before loading e.g. opaque identifiers.
// Since scope is determined by stack frames,
// this has the effect of ensuring that the package `st`
// becomes included in the identifier's scope.
(st @ Pkg(_), tok @ AirIdent(..)) => {
ctx.ret_or_transfer(st, tok, AirOpaqueAggregate::new())
}
(PkgOpaque(opaque), AirIdent(otok)) => ctx.proxy(opaque, otok),
(
PkgOpaque(_),
tok @ (AirExpr(..) | AirBind(..) | AirTpl(..) | AirDoc(..)),
) => {
// This is simply not expected at the time of writing,
// since this is used for importing object files.
crate::diagnostic_panic!(
vec![
tok.span()
.internal_error("this is not an opaque identifier"),
tok.span().help(
"this may represent a problem with an object file"
)
],
"expected opaque identifier, found {tok}",
);
}
(
st @ Root(_),
tok @ (AirExpr(..) | AirBind(..) | AirTpl(..) | AirDoc(..)),
@ -226,6 +280,7 @@ impl AirAggregate {
Pkg(st) => st.is_accepting(ctx),
PkgExpr(st) => st.is_accepting(ctx),
PkgTpl(st) => st.is_accepting(ctx),
PkgOpaque(st) => st.is_accepting(ctx),
}
}
@ -243,6 +298,7 @@ impl AirAggregate {
Pkg(st) => st.is_accepting(ctx),
PkgExpr(st) => st.is_accepting(ctx),
PkgTpl(st) => st.is_accepting(ctx),
PkgOpaque(st) => st.is_accepting(ctx),
}
}
@ -279,6 +335,12 @@ impl AirAggregate {
// Templates must therefore serve as containers for identifiers
// bound therein.
PkgTpl(tplst) => tplst.active_tpl_oi().map(Into::into),
// Loading of opaque objects happens within the context of the
// parent frame.
// At the time of writing,
// that is only a package.
PkgOpaque(_) => None,
}
}
@ -318,6 +380,10 @@ impl AirAggregate {
// This is not an environment.
(Uninit, kind) => kind,
// This is just a parsing state,
// not an environment.
(PkgOpaque(_), kind) => kind,
// Hidden is a fixpoint.
(_, kind @ Hidden(_)) => kind,
@ -563,6 +629,11 @@ impl AirAggregateCtx {
// since they may expand into an context where they are not
// considered to be dangling.
PkgTpl(tplst) => tplst.active_tpl_oi().map(Into::into),
// Expressions are transparent definitions,
// not opaque,
// and so not permitted in this context.
PkgOpaque(_) => None,
})
}
@ -579,6 +650,13 @@ impl AirAggregateCtx {
Pkg(pkg_st) => pkg_st.active_pkg_oi().map(Into::into),
PkgExpr(exprst) => exprst.active_expr_oi().map(Into::into),
PkgTpl(tplst) => tplst.active_tpl_oi().map(Into::into),
// Templates _could_ conceptually expand into opaque objects,
// but the source language of TAME provides no mechanism to do
// such a thing,
// and so it'd be best to leave this alone unless it's
// actually needed.
PkgOpaque(_) => None,
})
}
@ -665,7 +743,6 @@ impl AirAggregateCtx {
let Self { asg, stack, .. } = self;
let oi_ident = asg.create(Ident::declare(name));
// TODO: This will need the active OI to support `AirIdent`s
stack
.iter()
.rev()

View File

@ -824,13 +824,13 @@ sum_ir! {
}
}
/// Package definitions also capable of being loaded from object files.
/// Package definitions.
///
/// It is assumed that tokens that may appear as the body of a package,
/// with the exception of [`AirIdent`],
/// will preempt the package parser,
/// and so are not included here.
pub sum enum AirLoadablePkg = AirPkg | AirBind | AirIdent | AirDoc;
pub sum enum AirBindablePkg = AirPkg | AirBind | AirDoc;
/// Expressions that are able to be bound to identifiers.
///

View File

@ -0,0 +1,105 @@
// 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::*;
use std::fmt::Display;
#[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);
oi_from.add_opaque_dep(ctx.asg_mut(), oi_to);
Transition(Ready).incomplete()
}
(Ready, IdentFragment(name, text)) => ctx
.lookup_lexical_or_missing(name)
.set_fragment(ctx.asg_mut(), text)
.map(|_| ())
.transition(Ready),
(Ready, IdentRoot(name)) => {
ctx.lookup_lexical_or_missing(name).root(ctx.asg_mut());
Transition(Ready).incomplete()
}
}
}
fn is_accepting(&self, _: &Self::Context) -> bool {
matches!(self, Self::Ready)
}
}
impl AirOpaqueAggregate {
pub(super) fn new() -> Self {
Self::Ready
}
}

View File

@ -23,7 +23,7 @@
use super::{
super::{graph::object::Pkg, AsgError, ObjectIndex},
ir::AirLoadablePkg,
ir::AirBindablePkg,
AirAggregate, AirAggregateCtx,
};
use crate::{diagnose::Annotate, diagnostic_todo, parse::prelude::*};
@ -59,7 +59,7 @@ impl Display for AirPkgAggregate {
}
impl ParseState for AirPkgAggregate {
type Token = AirLoadablePkg;
type Token = AirBindablePkg;
type Object = ();
type Error = AsgError;
type Context = AirAggregateCtx;
@ -70,8 +70,8 @@ impl ParseState for AirPkgAggregate {
tok: Self::Token,
ctx: &mut Self::Context,
) -> crate::parse::TransitionResult<Self::Super> {
use super::ir::{AirBind::*, AirDoc::*, AirIdent::*, AirPkg::*};
use AirLoadablePkg::*;
use super::ir::{AirBind::*, AirDoc::*, AirPkg::*};
use AirBindablePkg::*;
use AirPkgAggregate::*;
match (self, tok) {
@ -128,45 +128,12 @@ impl ParseState for AirPkgAggregate {
.map(|_| ())
.transition(Toplevel(oi_pkg)),
(Toplevel(oi_pkg), AirIdent(IdentDecl(name, kind, src))) => ctx
.lookup_lexical_or_missing(name)
.declare(ctx.asg_mut(), name, kind, src)
.map(|_| ())
.transition(Toplevel(oi_pkg)),
(Toplevel(oi_pkg), AirIdent(IdentExternDecl(name, kind, src))) => {
ctx.lookup_lexical_or_missing(name)
.declare_extern(ctx.asg_mut(), name, kind, src)
.map(|_| ())
.transition(Toplevel(oi_pkg))
}
(Toplevel(oi_pkg), AirIdent(IdentDep(name, dep))) => {
let oi_from = ctx.lookup_lexical_or_missing(name);
let oi_to = ctx.lookup_lexical_or_missing(dep);
oi_from.add_opaque_dep(ctx.asg_mut(), oi_to);
Transition(Toplevel(oi_pkg)).incomplete()
}
(Toplevel(oi_pkg), AirIdent(IdentFragment(name, text))) => ctx
.lookup_lexical_or_missing(name)
.set_fragment(ctx.asg_mut(), text)
.map(|_| ())
.transition(Toplevel(oi_pkg)),
(Toplevel(oi_pkg), AirIdent(IdentRoot(name))) => {
ctx.lookup_lexical_or_missing(name).root(ctx.asg_mut());
Transition(Toplevel(oi_pkg)).incomplete()
}
(Ready, AirPkg(PkgEnd(span))) => {
Transition(Ready).err(AsgError::InvalidPkgEndContext(span))
}
// Token may refer to a parent context.
(st @ Ready, tok @ (AirBind(..) | AirIdent(..) | AirDoc(..))) => {
(st @ Ready, tok @ (AirBind(..) | AirDoc(..))) => {
Transition(st).dead(tok)
}
}

View File

@ -361,13 +361,13 @@ test_scopes! {
#[test]
opaque_a == [
(Root, S0, Visible),
// TODO: (Pkg, m(S1, S3), Visible),
(Pkg, m(S1, S3), Visible),
];
#[test]
opaque_b == [
(Root, S0, Visible),
// TODO: (Pkg, m(S4, S6), Visible),
(Pkg, m(S4, S6), Visible),
];
}