tame/tamer/src/nir/air.rs

168 lines
4.8 KiB
Rust
Raw Normal View History

// Lower NIR into AIR
//
// 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/>.
//! Lower [NIR](super) into [AIR](crate::asg::air).
use std::{error::Error, fmt::Display};
use crate::{
asg::{air::Air, ExprOp},
diagnose::Diagnostic,
nir::NirEntity,
parse::prelude::*,
tamer: asg::air: AIR as a sum IR This introduces a new macro `sum_ir!` to help with a long-standing problem of not being able to easily narrow types in Rust without a whole lot of boilerplate. This patch includes a bit of documentation, so see that for more information. This was not a welcome change---I jumped down this rabbit hole trying to decompose `AirAggregate` so that I can share portions of parsing with the current parser and a template parser. I can now proceed with that. This is not the only implementation that I had tried. I previously inverted the approach, as I've been doing manually for some time: manually create types to hold the sets of variants, and then create a sum type to hold those types. That works, but it resulted in a mess for systems that have to use the IR, since now you have two enums to contend with. I didn't find that to be appropriate, because we shouldn't complicate the external API for implementation details. The enum for IRs is supposed to be like a bytecode---a list of operations that can be performed with the IR. They can be grouped if it makes sense for a public API, but in my case, I only wanted subsets for the sake of delegating responsibilities to smaller subsystems, while retaining the context that `match` provides via its exhaustiveness checking but does not expose as something concrete (which is deeply frustrating!). Anyway, here we are; this'll be refined over time, hopefully, and portions of it can be generalized for removing boilerplate from other IRs. Another thing to note is that this syntax is really a compromise---I had to move on, and I was spending too much time trying to get creative with `macro_rules!`. It isn't the best, and it doesn't seem very Rust-like in some places and is therefore not necessarily all that intuitive. This can be refined further in the future. But the end result, all things considered, isn't too bad. DEV-13708
2023-03-02 15:15:28 -05:00
span::UNKNOWN_SPAN,
};
use super::Nir;
#[derive(Debug, PartialEq, Eq, Default)]
pub enum NirToAir {
#[default]
Ready,
}
impl Display for NirToAir {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use NirToAir::*;
match self {
Ready => write!(f, "ready to lower NIR to AIR"),
}
}
}
impl ParseState for NirToAir {
type Token = Nir;
type Object = Air;
type Error = NirToAirError;
fn parse_token(
self,
tok: Self::Token,
_: NoContext,
) -> TransitionResult<Self::Super> {
use NirToAir::*;
#[cfg(not(feature = "wip-nir-to-air"))]
{
let _ = tok; // prevent `unused_variables` warning
tamer: asg::air: AIR as a sum IR This introduces a new macro `sum_ir!` to help with a long-standing problem of not being able to easily narrow types in Rust without a whole lot of boilerplate. This patch includes a bit of documentation, so see that for more information. This was not a welcome change---I jumped down this rabbit hole trying to decompose `AirAggregate` so that I can share portions of parsing with the current parser and a template parser. I can now proceed with that. This is not the only implementation that I had tried. I previously inverted the approach, as I've been doing manually for some time: manually create types to hold the sets of variants, and then create a sum type to hold those types. That works, but it resulted in a mess for systems that have to use the IR, since now you have two enums to contend with. I didn't find that to be appropriate, because we shouldn't complicate the external API for implementation details. The enum for IRs is supposed to be like a bytecode---a list of operations that can be performed with the IR. They can be grouped if it makes sense for a public API, but in my case, I only wanted subsets for the sake of delegating responsibilities to smaller subsystems, while retaining the context that `match` provides via its exhaustiveness checking but does not expose as something concrete (which is deeply frustrating!). Anyway, here we are; this'll be refined over time, hopefully, and portions of it can be generalized for removing boilerplate from other IRs. Another thing to note is that this syntax is really a compromise---I had to move on, and I was spending too much time trying to get creative with `macro_rules!`. It isn't the best, and it doesn't seem very Rust-like in some places and is therefore not necessarily all that intuitive. This can be refined further in the future. But the end result, all things considered, isn't too bad. DEV-13708
2023-03-02 15:15:28 -05:00
return Transition(Ready).ok(Air::Todo(UNKNOWN_SPAN));
}
#[allow(unreachable_code)] // due to wip-nir-to-air
match (self, tok) {
(Ready, Nir::Open(NirEntity::Package, span)) => {
Transition(Ready).ok(Air::PkgOpen(span))
}
(Ready, Nir::Close(NirEntity::Package, span)) => {
Transition(Ready).ok(Air::PkgClose(span))
}
(Ready, Nir::Open(NirEntity::Rate | NirEntity::Sum, span)) => {
Transition(Ready).ok(Air::ExprOpen(ExprOp::Sum, span))
}
(Ready, Nir::Open(NirEntity::Product, span)) => {
Transition(Ready).ok(Air::ExprOpen(ExprOp::Product, span))
}
(Ready, Nir::Open(NirEntity::Ceil, span)) => {
Transition(Ready).ok(Air::ExprOpen(ExprOp::Ceil, span))
}
(Ready, Nir::Open(NirEntity::Floor, span)) => {
Transition(Ready).ok(Air::ExprOpen(ExprOp::Floor, span))
}
(Ready, Nir::Open(NirEntity::Classify | NirEntity::All, span)) => {
Transition(Ready).ok(Air::ExprOpen(ExprOp::Conj, span))
}
(Ready, Nir::Open(NirEntity::Any, span)) => {
Transition(Ready).ok(Air::ExprOpen(ExprOp::Disj, span))
}
(Ready, Nir::Open(NirEntity::Tpl, span)) => {
Transition(Ready).ok(Air::TplOpen(span))
}
(Ready, Nir::Close(NirEntity::Tpl, span)) => {
Transition(Ready).ok(Air::TplClose(span))
}
(
Ready,
Nir::Close(
NirEntity::Rate
| NirEntity::Sum
| NirEntity::Product
| NirEntity::Ceil
| NirEntity::Floor
| NirEntity::Classify
| NirEntity::All
| NirEntity::Any,
span,
),
) => Transition(Ready).ok(Air::ExprClose(span)),
(Ready, Nir::BindIdent(spair)) => {
Transition(Ready).ok(Air::BindIdent(spair))
}
(
Ready,
Nir::Todo
| Nir::TodoAttr(..)
| Nir::Ref(..)
| Nir::Desc(..)
| Nir::Text(_)
| Nir::Open(NirEntity::TplParam, _)
| Nir::Close(NirEntity::TplParam, _),
tamer: asg::air: AIR as a sum IR This introduces a new macro `sum_ir!` to help with a long-standing problem of not being able to easily narrow types in Rust without a whole lot of boilerplate. This patch includes a bit of documentation, so see that for more information. This was not a welcome change---I jumped down this rabbit hole trying to decompose `AirAggregate` so that I can share portions of parsing with the current parser and a template parser. I can now proceed with that. This is not the only implementation that I had tried. I previously inverted the approach, as I've been doing manually for some time: manually create types to hold the sets of variants, and then create a sum type to hold those types. That works, but it resulted in a mess for systems that have to use the IR, since now you have two enums to contend with. I didn't find that to be appropriate, because we shouldn't complicate the external API for implementation details. The enum for IRs is supposed to be like a bytecode---a list of operations that can be performed with the IR. They can be grouped if it makes sense for a public API, but in my case, I only wanted subsets for the sake of delegating responsibilities to smaller subsystems, while retaining the context that `match` provides via its exhaustiveness checking but does not expose as something concrete (which is deeply frustrating!). Anyway, here we are; this'll be refined over time, hopefully, and portions of it can be generalized for removing boilerplate from other IRs. Another thing to note is that this syntax is really a compromise---I had to move on, and I was spending too much time trying to get creative with `macro_rules!`. It isn't the best, and it doesn't seem very Rust-like in some places and is therefore not necessarily all that intuitive. This can be refined further in the future. But the end result, all things considered, isn't too bad. DEV-13708
2023-03-02 15:15:28 -05:00
) => Transition(Ready).ok(Air::Todo(UNKNOWN_SPAN)),
}
}
fn is_accepting(&self, _: &Self::Context) -> bool {
true
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum NirToAirError {
Todo,
}
impl Display for NirToAirError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use NirToAirError::*;
match self {
Todo => write!(f, "TODO"),
}
}
}
impl Error for NirToAirError {}
impl Diagnostic for NirToAirError {
fn describe(&self) -> Vec<crate::diagnose::AnnotatedSpan> {
// TODO
vec![]
}
}
#[cfg(all(test, feature = "wip-nir-to-air"))]
mod test;