302 lines
8.4 KiB
Rust
302 lines
8.4 KiB
Rust
// Expressions represented on the 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/>.
|
|
|
|
//! Expressions on the ASG.
|
|
|
|
use std::fmt::Display;
|
|
|
|
use super::{
|
|
Asg, Ident, Object, ObjectIndex, ObjectRel, ObjectRelFrom, ObjectRelTy,
|
|
ObjectRelatable,
|
|
};
|
|
use crate::{
|
|
f::Functor,
|
|
num::Dim,
|
|
parse::{util::SPair, Token},
|
|
span::Span,
|
|
};
|
|
|
|
#[cfg(doc)]
|
|
use super::ObjectKind;
|
|
|
|
/// Expression.
|
|
///
|
|
/// The [`Span`] of an expression should be expanded to encompass not only
|
|
/// all child expressions,
|
|
/// but also any applicable closing span.
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
pub struct Expr(ExprOp, ExprDim, Span);
|
|
|
|
impl Expr {
|
|
pub fn new(op: ExprOp, span: Span) -> Self {
|
|
Self(op, ExprDim::default(), span)
|
|
}
|
|
|
|
pub fn span(&self) -> Span {
|
|
match self {
|
|
Expr(_, _, span) => *span,
|
|
}
|
|
}
|
|
|
|
pub fn op(&self) -> ExprOp {
|
|
match self {
|
|
Expr(op, _, _) => *op,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Functor<Span> for Expr {
|
|
fn map(self, f: impl FnOnce(Span) -> Span) -> Self {
|
|
match self {
|
|
Self(op, dim, span) => Self(op, dim, f(span)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<&Expr> for Span {
|
|
fn from(val: &Expr) -> Self {
|
|
val.span()
|
|
}
|
|
}
|
|
|
|
impl Display for Expr {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
match self {
|
|
Self(op, dim, _) => write!(f, "{op} expression with {dim}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Expression operation type.
|
|
///
|
|
/// TODO: Ideally this will be replaced with arbitrary binary (dyadic)
|
|
/// functions defined within the language of TAME itself,
|
|
/// as was the original plan with TAMER.
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
pub enum ExprOp {
|
|
Sum,
|
|
Product,
|
|
}
|
|
|
|
impl Display for ExprOp {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
use ExprOp::*;
|
|
|
|
match self {
|
|
Sum => write!(f, "sum"),
|
|
Product => write!(f, "product"),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The dimensionality of the expression itself and the target
|
|
/// dimensionality required by its context.
|
|
///
|
|
/// If the target dimensionality is greater,
|
|
/// then the expression's value will be broadcast;
|
|
/// if it is less,
|
|
/// than it will be folded according to the expression's [`ExprOp`].
|
|
///
|
|
/// A value of [`None`] means that the dimensionality has not yet been
|
|
/// constrained either through inference or explicit specification by the
|
|
/// user.
|
|
#[derive(Debug, PartialEq, Eq, Default)]
|
|
pub struct ExprDim(InnerDim, OuterDim);
|
|
|
|
impl Display for ExprDim {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
match self {
|
|
Self(inner, outer) => write!(f, "{inner} and {outer}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Dimensionality of the inner expression.
|
|
///
|
|
/// This represents the dimensionality after any necessary broadcasting of
|
|
/// all values referenced by this expression.
|
|
/// This does not necessarily correspond to the dimensionality of the
|
|
/// final value of this expression;
|
|
/// see [`OuterDim`].
|
|
#[derive(Debug, PartialEq, Eq, Default)]
|
|
pub struct InnerDim(DimState);
|
|
|
|
impl Display for InnerDim {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
match self {
|
|
Self(dim_st) => write!(f, "inner dimensionality {dim_st}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Final dimensionality of the expression as observed by others.
|
|
///
|
|
/// This represents what other expressions will see as the dimensionality of
|
|
/// this expression.
|
|
/// For example,
|
|
/// [`InnerDim`] may require broadcasting,
|
|
/// but this outer dimensionality may require subsequent folding.
|
|
#[derive(Debug, PartialEq, Eq, Default)]
|
|
pub struct OuterDim(DimState);
|
|
|
|
impl Display for OuterDim {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
match self {
|
|
Self(dim_st) => write!(f, "outer dimensionality {dim_st}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Default)]
|
|
pub enum DimState {
|
|
/// Dimensionality has been explicitly constrained via user
|
|
/// specification and cannot be changed.
|
|
///
|
|
/// The provided [`Span`] references the location of the user-specified
|
|
/// constraint.
|
|
/// Unification must honor this value.
|
|
_Constraint(Dim, Span),
|
|
|
|
/// Dimensionality is still being inferred,
|
|
/// but it is known to be at least this size.
|
|
///
|
|
/// The provide [`Span`] must serve as evidence for this inference by
|
|
/// referencing a value of this dimensionality.
|
|
_AtLeast(Dim, Span),
|
|
|
|
/// Dimensionality is not constrained and has not yet been inferred.
|
|
///
|
|
/// This also serves as a TODO until we infer dimensions.
|
|
#[default]
|
|
Unknown,
|
|
}
|
|
|
|
impl Display for DimState {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
use DimState::*;
|
|
|
|
match self {
|
|
_Constraint(dim, _) => write!(f, "constrained to {dim}"),
|
|
_AtLeast(dim, _) => write!(f, "of at least {dim}"),
|
|
Unknown => write!(f, "unknown"),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Subset of [`ObjectKind`]s that are valid targets for edges from
|
|
/// [`Expr`].
|
|
///
|
|
/// See [`ObjectRel`] for more information.
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
pub enum ExprRel {
|
|
Ident(ObjectIndex<Ident>),
|
|
Expr(ObjectIndex<Expr>),
|
|
}
|
|
|
|
impl ObjectRel<Expr> for ExprRel {
|
|
fn narrow<OB: ObjectRelFrom<Expr> + ObjectRelatable>(
|
|
self,
|
|
) -> Option<ObjectIndex<OB>> {
|
|
match self {
|
|
Self::Ident(oi) => oi.filter_rel(),
|
|
Self::Expr(oi) => oi.filter_rel(),
|
|
}
|
|
}
|
|
|
|
/// Whether this is a cross edge to another tree.
|
|
///
|
|
/// An expression is inherently a tree,
|
|
/// however it may contain references to other identifiers which
|
|
/// represent their own trees.
|
|
/// Any [`Ident`] reference is a cross edge.
|
|
fn is_cross_edge(&self) -> bool {
|
|
match self {
|
|
Self::Ident(..) => true,
|
|
Self::Expr(..) => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ObjectRelatable for Expr {
|
|
type Rel = ExprRel;
|
|
|
|
fn rel_ty() -> ObjectRelTy {
|
|
ObjectRelTy::Expr
|
|
}
|
|
|
|
fn new_rel_dyn(
|
|
ty: ObjectRelTy,
|
|
oi: ObjectIndex<Object>,
|
|
) -> Option<ExprRel> {
|
|
match ty {
|
|
ObjectRelTy::Root => None,
|
|
ObjectRelTy::Pkg => None,
|
|
ObjectRelTy::Ident => Some(ExprRel::Ident(oi.must_narrow_into())),
|
|
ObjectRelTy::Expr => Some(ExprRel::Expr(oi.must_narrow_into())),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<ObjectIndex<Ident>> for ExprRel {
|
|
fn from(value: ObjectIndex<Ident>) -> Self {
|
|
Self::Ident(value)
|
|
}
|
|
}
|
|
|
|
impl From<ObjectIndex<Expr>> for ExprRel {
|
|
fn from(value: ObjectIndex<Expr>) -> Self {
|
|
Self::Expr(value)
|
|
}
|
|
}
|
|
|
|
impl ObjectIndex<Expr> {
|
|
/// Create a new subexpression as the next child of this expression and
|
|
/// return the [`ObjectIndex`] of the new subexpression.
|
|
///
|
|
/// Sub-expressions maintain relative order to accommodate
|
|
/// non-associative and non-commutative expressions.
|
|
pub fn create_subexpr(
|
|
self,
|
|
asg: &mut Asg,
|
|
expr: Expr,
|
|
) -> ObjectIndex<Expr> {
|
|
let oi_subexpr = asg.create(expr);
|
|
oi_subexpr.add_edge_from(asg, self, None)
|
|
}
|
|
|
|
/// Reference the value of the expression identified by `ident` as if it
|
|
/// were a subexpression.
|
|
///
|
|
/// If `ident` does not yet exist on the graph,
|
|
/// a missing identifier will take its place to be later resolved once
|
|
/// it becomes available.
|
|
pub fn ref_expr(self, asg: &mut Asg, ident: SPair) -> Self {
|
|
let identi = asg.lookup_or_missing(ident);
|
|
self.add_edge_to(asg, identi, Some(ident.span()))
|
|
}
|
|
|
|
/// The [`Ident`] bound to this expression,
|
|
/// if any.
|
|
pub fn ident(self, asg: &Asg) -> Option<&Ident> {
|
|
self.incoming_edges_filtered(asg)
|
|
.map(ObjectIndex::cresolve(asg))
|
|
.next()
|
|
}
|
|
}
|