diff --git a/tamer/src/asg/air.rs b/tamer/src/asg/air.rs index ada3dcb2..065c327d 100644 --- a/tamer/src/asg/air.rs +++ b/tamer/src/asg/air.rs @@ -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 for AirAggregate { } } +impl From 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() diff --git a/tamer/src/asg/air/ir.rs b/tamer/src/asg/air/ir.rs index 5a48cfed..844d1af8 100644 --- a/tamer/src/asg/air/ir.rs +++ b/tamer/src/asg/air/ir.rs @@ -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. /// diff --git a/tamer/src/asg/air/opaque.rs b/tamer/src/asg/air/opaque.rs new file mode 100644 index 00000000..bc3f4914 --- /dev/null +++ b/tamer/src/asg/air/opaque.rs @@ -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 . + +//! 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 { + 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 + } +} diff --git a/tamer/src/asg/air/pkg.rs b/tamer/src/asg/air/pkg.rs index 9120a4fc..8d607f4a 100644 --- a/tamer/src/asg/air/pkg.rs +++ b/tamer/src/asg/air/pkg.rs @@ -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 { - 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) } } diff --git a/tamer/src/asg/air/test/scope.rs b/tamer/src/asg/air/test/scope.rs index fbc09de9..4f37e1bb 100644 --- a/tamer/src/asg/air/test/scope.rs +++ b/tamer/src/asg/air/test/scope.rs @@ -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), ]; }