geam 0.1.1

Experimental Rust-embedded execution runtime for typed Gleam programs
use crate::plan::{BoolExpr, Expr};
use crate::planner::context::PlanContext;
use crate::planner::error::PlanError;
use gleam_core::ast::TypedExpr;

pub(super) fn lt(
    left: TypedExpr,
    right: TypedExpr,
    context: &mut PlanContext<'_>,
) -> Result<Expr, PlanError> {
    Ok(Expr::bool(BoolExpr::lt_int(
        super::super::plan_int_expr(left, context)?,
        super::super::plan_int_expr(right, context)?,
    )))
}

pub(super) fn lt_float(
    left: TypedExpr,
    right: TypedExpr,
    context: &mut PlanContext<'_>,
) -> Result<Expr, PlanError> {
    Ok(Expr::bool(BoolExpr::lt_float(
        super::super::plan_float_expr(left, context)?,
        super::super::plan_float_expr(right, context)?,
    )))
}

pub(super) fn lte(
    left: TypedExpr,
    right: TypedExpr,
    context: &mut PlanContext<'_>,
) -> Result<Expr, PlanError> {
    Ok(Expr::bool(BoolExpr::lte_int(
        super::super::plan_int_expr(left, context)?,
        super::super::plan_int_expr(right, context)?,
    )))
}

pub(super) fn lte_float(
    left: TypedExpr,
    right: TypedExpr,
    context: &mut PlanContext<'_>,
) -> Result<Expr, PlanError> {
    Ok(Expr::bool(BoolExpr::lte_float(
        super::super::plan_float_expr(left, context)?,
        super::super::plan_float_expr(right, context)?,
    )))
}

pub(super) fn gt(
    left: TypedExpr,
    right: TypedExpr,
    context: &mut PlanContext<'_>,
) -> Result<Expr, PlanError> {
    Ok(Expr::bool(BoolExpr::gt_int(
        super::super::plan_int_expr(left, context)?,
        super::super::plan_int_expr(right, context)?,
    )))
}

pub(super) fn gt_float(
    left: TypedExpr,
    right: TypedExpr,
    context: &mut PlanContext<'_>,
) -> Result<Expr, PlanError> {
    Ok(Expr::bool(BoolExpr::gt_float(
        super::super::plan_float_expr(left, context)?,
        super::super::plan_float_expr(right, context)?,
    )))
}

pub(super) fn gte(
    left: TypedExpr,
    right: TypedExpr,
    context: &mut PlanContext<'_>,
) -> Result<Expr, PlanError> {
    Ok(Expr::bool(BoolExpr::gte_int(
        super::super::plan_int_expr(left, context)?,
        super::super::plan_int_expr(right, context)?,
    )))
}

pub(super) fn gte_float(
    left: TypedExpr,
    right: TypedExpr,
    context: &mut PlanContext<'_>,
) -> Result<Expr, PlanError> {
    Ok(Expr::bool(BoolExpr::gte_float(
        super::super::plan_float_expr(left, context)?,
        super::super::plan_float_expr(right, context)?,
    )))
}

#[cfg(test)]
mod tests {
    use crate::planner::dsl::{float, function, int, module};
    use crate::planner::plan_module;
    use crate::planner::support::{compile, expect_plan_error};
    use crate::planner::{PlanError, UnsupportedBitArraySegmentReason};

    #[test]
    fn plan_integer_ordering() {
        let actual = plan_module(compile(
            r#"
pub fn main() {
  1 < 2
}

pub fn lte() {
  1 <= 2
}

pub fn gt() {
  2 > 1
}

pub fn gte() {
  2 >= 1
}
"#,
        ))
        .expect("source should plan");
        let expected = module(
            "main",
            function("main", int(1).lt_int(int(2))),
            [
                function("lte", int(1).lte_int(int(2))),
                function("gt", int(2).gt_int(int(1))),
                function("gte", int(2).gte_int(int(1))),
            ],
        );

        assert_eq!(actual, expected);
    }

    #[test]
    fn plan_float_ordering() {
        let actual = plan_module(compile(
            r#"
pub fn main() {
  1.0 <. 2.0
}

pub fn lte() {
  1.0 <=. 2.0
}

pub fn gt() {
  2.0 >. 1.0
}

pub fn gte() {
  2.0 >=. 1.0
}
"#,
        ))
        .expect("source should plan");
        let expected = module(
            "main",
            function("main", float(1.0).lt_float(float(2.0))),
            [
                function("lte", float(1.0).lte_float(float(2.0))),
                function("gt", float(2.0).gt_float(float(1.0))),
                function("gte", float(2.0).gte_float(float(1.0))),
            ],
        );

        assert_eq!(actual, expected);
    }

    #[test]
    fn reject_profile_ordering_operand_expression_errors_propagate() {
        for (name, src) in [
            (
                "lt int left",
                r#"
pub fn main() {
  {
    <<1:native>>
    1
  } < 1
}
"#,
            ),
            (
                "lt int right",
                r#"
pub fn main() {
  1 < {
    <<1:native>>
    1
  }
}
"#,
            ),
            (
                "lte int left",
                r#"
pub fn main() {
  {
    <<1:native>>
    1
  } <= 1
}
"#,
            ),
            (
                "lte int right",
                r#"
pub fn main() {
  1 <= {
    <<1:native>>
    1
  }
}
"#,
            ),
            (
                "gt int left",
                r#"
pub fn main() {
  {
    <<1:native>>
    1
  } > 1
}
"#,
            ),
            (
                "gt int right",
                r#"
pub fn main() {
  1 > {
    <<1:native>>
    1
  }
}
"#,
            ),
            (
                "gte int left",
                r#"
pub fn main() {
  {
    <<1:native>>
    1
  } >= 1
}
"#,
            ),
            (
                "gte int right",
                r#"
pub fn main() {
  1 >= {
    <<1:native>>
    1
  }
}
"#,
            ),
            (
                "lt float left",
                r#"
pub fn main() {
  {
    <<1:native>>
    1.0
  } <. 1.0
}
"#,
            ),
            (
                "lt float right",
                r#"
pub fn main() {
  1.0 <. {
    <<1:native>>
    1.0
  }
}
"#,
            ),
            (
                "lte float left",
                r#"
pub fn main() {
  {
    <<1:native>>
    1.0
  } <=. 1.0
}
"#,
            ),
            (
                "lte float right",
                r#"
pub fn main() {
  1.0 <=. {
    <<1:native>>
    1.0
  }
}
"#,
            ),
            (
                "gt float left",
                r#"
pub fn main() {
  {
    <<1:native>>
    1.0
  } >. 1.0
}
"#,
            ),
            (
                "gt float right",
                r#"
pub fn main() {
  1.0 >. {
    <<1:native>>
    1.0
  }
}
"#,
            ),
            (
                "gte float left",
                r#"
pub fn main() {
  {
    <<1:native>>
    1.0
  } >=. 1.0
}
"#,
            ),
            (
                "gte float right",
                r#"
pub fn main() {
  1.0 >=. {
    <<1:native>>
    1.0
  }
}
"#,
            ),
        ] {
            assert_native_endianness_rejected(name, src);
        }
    }

    fn assert_native_endianness_rejected(name: &str, src: &str) {
        assert_eq!(
            expect_plan_error(src),
            PlanError::UnsupportedBitArraySegment {
                reason: UnsupportedBitArraySegmentReason::NativeEndianness,
            },
            "{name}",
        );
    }
}