starlark/src/eval/fragment/expr_bool.rs (133 lines of code) (raw):
/*
* Copyright 2019 The Starlark in Rust Authors.
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! Boolean expression.
use crate::{
eval::{
fragment::{expr::ExprCompiled, span::IrSpanned},
runtime::call_stack::FrozenFileSpan,
},
values::FrozenValue,
};
/// Boolean expression.
pub(crate) enum ExprCompiledBool {
Const(bool),
/// Non-const expression.
Expr(ExprCompiled),
}
impl IrSpanned<ExprCompiledBool> {
pub(crate) fn into_expr(self) -> IrSpanned<ExprCompiled> {
IrSpanned {
span: self.span,
node: self.node.into_expr(),
}
}
}
impl ExprCompiledBool {
fn into_expr(self) -> ExprCompiled {
match self {
ExprCompiledBool::Const(b) => ExprCompiled::Value(FrozenValue::new_bool(b)),
ExprCompiledBool::Expr(e) => e,
}
}
fn const_value(&self) -> Option<bool> {
match self {
ExprCompiledBool::Const(b) => Some(*b),
ExprCompiledBool::Expr(..) => None,
}
}
/// `bool(x)` and do trivial optimizations.
pub(crate) fn new(expr: IrSpanned<ExprCompiled>) -> IrSpanned<ExprCompiledBool> {
fn new_bool(span: FrozenFileSpan, b: bool) -> IrSpanned<ExprCompiledBool> {
IrSpanned {
node: ExprCompiledBool::Const(b),
span,
}
}
let span = expr.span;
if let Some(b) = expr.is_pure_infallible_to_bool() {
return new_bool(span, b);
}
match expr.node {
ExprCompiled::Not(box x) => {
let x = Self::new(x);
match x.const_value() {
Some(b) => new_bool(span, !b),
None => IrSpanned {
node: ExprCompiledBool::Expr(ExprCompiled::Not(box x.into_expr())),
span,
},
}
}
ExprCompiled::And(box (x, y)) => {
let x = Self::new(x);
let y = Self::new(y);
match (x.const_value(), y.const_value()) {
(Some(false), _) => new_bool(span, false),
(Some(true), _) => y,
(None, Some(true)) => x,
(None, Some(false)) => {
// The expression evaluates to false,
// but we need to preserve LHS for the effect.
IrSpanned {
span,
node: ExprCompiledBool::Expr(
ExprCompiled::seq(
x.into_expr(),
new_bool(y.span, false).into_expr(),
)
.node,
),
}
}
(None, None) => IrSpanned {
node: ExprCompiledBool::Expr(ExprCompiled::And(box (
x.into_expr(),
y.into_expr(),
))),
span,
},
}
}
// "Or" handling is very similar to "and",
// not folding to keep it readable.
ExprCompiled::Or(box (x, y)) => {
let x = Self::new(x);
let y = Self::new(y);
match (x.const_value(), y.const_value()) {
(Some(true), _) => new_bool(span, true),
(Some(false), _) => y,
(None, Some(false)) => x,
(None, Some(true)) => {
// The expression evaluates to true,
// but we need to preserve LHS for the effect.
IrSpanned {
span,
node: ExprCompiledBool::Expr(
ExprCompiled::seq(
x.into_expr(),
new_bool(y.span, true).into_expr(),
)
.node,
),
}
}
(None, None) => IrSpanned {
node: ExprCompiledBool::Expr(ExprCompiled::Or(box (
x.into_expr(),
y.into_expr(),
))),
span,
},
}
}
expr => IrSpanned {
node: ExprCompiledBool::Expr(expr),
span,
},
}
}
}