2023-03-07 14:00:46 -05:00
|
|
|
// ASG IR template 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 template parser.
|
|
|
|
//!
|
|
|
|
//! See the [parent module](super) for more information.
|
|
|
|
|
|
|
|
use super::{
|
|
|
|
super::{
|
|
|
|
graph::object::{Pkg, Tpl},
|
|
|
|
Asg, AsgError, ObjectIndex,
|
|
|
|
},
|
2023-03-08 11:18:51 -05:00
|
|
|
expr::AirExprAggregateStoreDangling,
|
2023-03-11 00:40:54 -05:00
|
|
|
ir::AirTemplatable,
|
|
|
|
AirExprAggregate,
|
2023-03-07 14:00:46 -05:00
|
|
|
};
|
|
|
|
use crate::{
|
|
|
|
fmt::{DisplayWrapper, TtQuote},
|
|
|
|
parse::prelude::*,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Template parser and token aggregator.
|
|
|
|
///
|
|
|
|
/// A template consists of
|
|
|
|
///
|
|
|
|
/// - Metadata about the template,
|
|
|
|
/// including its parameters; and
|
2023-03-11 00:40:54 -05:00
|
|
|
/// - A collection of [`AirTemplatable`] tokens representing the body of
|
|
|
|
/// the template that will be expanded into the application site when
|
|
|
|
/// the template is applied.
|
2023-03-07 14:00:46 -05:00
|
|
|
///
|
|
|
|
/// This contains an embedded [`AirExprAggregate`] parser for handling
|
|
|
|
/// expressions just the same as [`super::AirAggregate`] does with
|
|
|
|
/// packages.
|
2023-03-07 16:28:32 -05:00
|
|
|
#[derive(Debug, PartialEq)]
|
2023-03-07 14:00:46 -05:00
|
|
|
pub enum AirTplAggregate {
|
|
|
|
/// Ready for a template,
|
|
|
|
/// defined as part of the given package.
|
|
|
|
///
|
2023-03-07 16:28:32 -05:00
|
|
|
/// This state also includes the template header;
|
2023-03-07 14:00:46 -05:00
|
|
|
/// unlike NIR,
|
|
|
|
/// AIR has no restrictions on when template header tokens are
|
|
|
|
/// provided,
|
|
|
|
/// which simplifies AIR generation.
|
2023-03-07 16:28:32 -05:00
|
|
|
Ready(ObjectIndex<Pkg>),
|
2023-03-07 14:00:46 -05:00
|
|
|
|
2023-03-08 14:47:31 -05:00
|
|
|
Toplevel(
|
|
|
|
ObjectIndex<Pkg>,
|
|
|
|
ObjectIndex<Tpl>,
|
|
|
|
AirExprAggregateStoreDangling<Tpl>,
|
|
|
|
Option<SPair>,
|
|
|
|
),
|
|
|
|
|
2023-03-07 14:00:46 -05:00
|
|
|
/// Aggregating tokens into a template.
|
2023-03-08 14:47:31 -05:00
|
|
|
TplExpr(
|
2023-03-07 14:00:46 -05:00
|
|
|
ObjectIndex<Pkg>,
|
|
|
|
ObjectIndex<Tpl>,
|
2023-03-08 11:18:51 -05:00
|
|
|
AirExprAggregateStoreDangling<Tpl>,
|
2023-03-07 14:00:46 -05:00
|
|
|
Option<SPair>,
|
|
|
|
),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for AirTplAggregate {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
match self {
|
2023-03-07 16:28:32 -05:00
|
|
|
Self::Ready(_) => write!(f, "ready for template definition"),
|
2023-03-08 14:47:31 -05:00
|
|
|
|
|
|
|
Self::Toplevel(_, expr, _, None)
|
|
|
|
| Self::TplExpr(_, expr, _, None) => {
|
2023-03-07 14:00:46 -05:00
|
|
|
write!(f, "building anonymous template with {expr}")
|
|
|
|
}
|
2023-03-08 14:47:31 -05:00
|
|
|
|
|
|
|
Self::Toplevel(_, expr, _, Some(name))
|
|
|
|
| Self::TplExpr(_, expr, _, Some(name)) => {
|
2023-03-07 14:00:46 -05:00
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"building named template {} with {expr}",
|
|
|
|
TtQuote::wrap(name)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ParseState for AirTplAggregate {
|
2023-03-11 00:40:54 -05:00
|
|
|
type Token = AirTemplatable;
|
2023-03-07 14:00:46 -05:00
|
|
|
type Object = ();
|
|
|
|
type Error = AsgError;
|
|
|
|
type Context = Asg;
|
|
|
|
|
|
|
|
fn parse_token(
|
|
|
|
self,
|
|
|
|
tok: Self::Token,
|
|
|
|
asg: &mut Self::Context,
|
|
|
|
) -> TransitionResult<Self::Super> {
|
2023-03-11 00:40:54 -05:00
|
|
|
use super::ir::{AirBind::*, AirTpl::*};
|
|
|
|
use AirTemplatable::*;
|
2023-03-07 14:00:46 -05:00
|
|
|
use AirTplAggregate::*;
|
|
|
|
|
2023-03-11 00:40:54 -05:00
|
|
|
match (self, tok) {
|
2023-03-15 10:59:22 -04:00
|
|
|
(Ready(oi_pkg), AirTpl(TplStart(span))) => {
|
2023-03-07 14:00:46 -05:00
|
|
|
let oi_tpl = asg.create(Tpl::new(span));
|
|
|
|
|
2023-03-08 14:47:31 -05:00
|
|
|
Transition(Toplevel(
|
2023-03-07 16:28:32 -05:00
|
|
|
oi_pkg,
|
|
|
|
oi_tpl,
|
|
|
|
AirExprAggregate::new_in(oi_tpl),
|
|
|
|
None,
|
|
|
|
))
|
|
|
|
.incomplete()
|
2023-03-07 14:00:46 -05:00
|
|
|
}
|
|
|
|
|
2023-03-15 10:59:22 -04:00
|
|
|
(Toplevel(..), AirTpl(TplStart(_span))) => todo!("nested tpl open"),
|
2023-03-08 14:47:31 -05:00
|
|
|
|
|
|
|
(Toplevel(oi_pkg, oi_tpl, expr, _), AirBind(BindIdent(name))) => {
|
|
|
|
asg.lookup_or_missing(name)
|
|
|
|
.bind_definition(asg, name, oi_tpl)
|
|
|
|
.map(|oi_ident| oi_pkg.defines(asg, oi_ident))
|
|
|
|
.map(|_| ())
|
|
|
|
.transition(Toplevel(oi_pkg, oi_tpl, expr, Some(name)))
|
|
|
|
}
|
|
|
|
|
|
|
|
(Toplevel(..), AirBind(RefIdent(_))) => {
|
|
|
|
todo!("tpl Toplevel RefIdent")
|
|
|
|
}
|
2023-03-07 14:00:46 -05:00
|
|
|
|
2023-03-15 10:59:22 -04:00
|
|
|
(Toplevel(oi_pkg, oi_tpl, _expr_done, _), AirTpl(TplEnd(span))) => {
|
2023-03-07 14:00:46 -05:00
|
|
|
oi_tpl.close(asg, span);
|
2023-03-07 16:28:32 -05:00
|
|
|
Transition(Ready(oi_pkg)).incomplete()
|
2023-03-07 14:00:46 -05:00
|
|
|
}
|
|
|
|
|
2023-03-15 10:59:22 -04:00
|
|
|
(TplExpr(oi_pkg, oi_tpl, expr, name), AirTpl(TplEnd(span))) => {
|
2023-03-08 14:47:31 -05:00
|
|
|
// TODO: duplicated with AirAggregate
|
|
|
|
match expr.is_accepting(asg) {
|
|
|
|
true => {
|
|
|
|
// TODO: this is duplicated with the above
|
|
|
|
oi_tpl.close(asg, span);
|
|
|
|
Transition(Ready(oi_pkg)).incomplete()
|
|
|
|
}
|
|
|
|
false => Transition(TplExpr(oi_pkg, oi_tpl, expr, name))
|
2023-03-15 10:59:22 -04:00
|
|
|
.err(AsgError::InvalidTplEndContext(span)),
|
2023-03-08 14:47:31 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
(
|
|
|
|
Toplevel(oi_pkg, oi_tpl, expr, name)
|
|
|
|
| TplExpr(oi_pkg, oi_tpl, expr, name),
|
|
|
|
AirExpr(etok),
|
|
|
|
) => Self::delegate_expr(asg, oi_pkg, oi_tpl, expr, name, etok),
|
|
|
|
|
|
|
|
(TplExpr(oi_pkg, oi_tpl, expr, name), AirBind(etok)) => {
|
|
|
|
Self::delegate_expr(asg, oi_pkg, oi_tpl, expr, name, etok)
|
|
|
|
}
|
|
|
|
|
2023-03-15 10:59:22 -04:00
|
|
|
(TplExpr(..), AirTpl(TplStart(_))) => {
|
2023-03-08 14:47:31 -05:00
|
|
|
todo!("nested template (template-generated template)")
|
|
|
|
}
|
2023-03-07 14:00:46 -05:00
|
|
|
|
2023-03-15 10:59:22 -04:00
|
|
|
(st @ Ready(..), AirTpl(TplEnd(span))) => {
|
2023-03-07 14:00:46 -05:00
|
|
|
Transition(st).err(AsgError::UnbalancedTpl(span))
|
|
|
|
}
|
|
|
|
|
2023-03-11 00:40:54 -05:00
|
|
|
(st @ Ready(..), tok @ (AirExpr(..) | AirBind(..))) => {
|
|
|
|
Transition(st).dead(tok)
|
|
|
|
}
|
2023-03-07 14:00:46 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_accepting(&self, _: &Self::Context) -> bool {
|
|
|
|
matches!(self, Self::Ready(..))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AirTplAggregate {
|
|
|
|
pub(super) fn new_in_pkg(oi_pkg: ObjectIndex<Pkg>) -> Self {
|
2023-03-07 16:28:32 -05:00
|
|
|
Self::Ready(oi_pkg)
|
2023-03-07 14:00:46 -05:00
|
|
|
}
|
2023-03-08 14:47:31 -05:00
|
|
|
|
|
|
|
/// Delegate to the expression parser [`AirExprAggregate`].
|
|
|
|
// TODO: Sir, this argument count is out of control.
|
|
|
|
fn delegate_expr(
|
|
|
|
asg: &mut <Self as ParseState>::Context,
|
|
|
|
oi_pkg: ObjectIndex<Pkg>,
|
|
|
|
oi_tpl: ObjectIndex<Tpl>,
|
|
|
|
expr: AirExprAggregateStoreDangling<Tpl>,
|
|
|
|
name: Option<SPair>,
|
|
|
|
etok: impl Into<<AirExprAggregateStoreDangling<Tpl> as ParseState>::Token>,
|
|
|
|
) -> TransitionResult<Self> {
|
|
|
|
let tok = etok.into();
|
|
|
|
|
|
|
|
expr.parse_token(tok, asg).branch_dead::<Self, _>(
|
|
|
|
|expr, ()| {
|
|
|
|
Transition(Self::Toplevel(oi_pkg, oi_tpl, expr, name))
|
|
|
|
.incomplete()
|
|
|
|
},
|
|
|
|
|expr, result, ()| {
|
|
|
|
result
|
|
|
|
.map(ParseStatus::reflexivity)
|
|
|
|
.transition(Self::TplExpr(oi_pkg, oi_tpl, expr, name))
|
|
|
|
},
|
|
|
|
(),
|
|
|
|
)
|
|
|
|
}
|
2023-03-07 14:00:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test;
|