geam 0.1.1

Experimental Rust-embedded execution runtime for typed Gleam programs
use super::super::draft::pattern::{
    DraftBitArrayBindingPattern, DraftBitArrayPattern, DraftBitArrayPatternSegment,
    DraftBitArrayPatternSize, DraftBitArrayPatternSizeExpr, DraftBitArrayPatternValue,
    DraftBitArrayStringPattern, DraftMatchListTail, DraftMatchPattern, DraftMatchPatternBinding,
};
use super::value::BlockValues;
use crate::plan::execution;

pub(super) fn freeze(
    pattern: DraftMatchPattern,
    values: &BlockValues,
) -> execution::graph::MatchPattern {
    use execution::graph::MatchPattern as E;

    match pattern {
        DraftMatchPattern::Bind(binding) => E::Bind(freeze_binding(binding)),
        DraftMatchPattern::Discard => E::Discard,
        DraftMatchPattern::Int(value) => E::Int(value),
        DraftMatchPattern::Float(value) => E::Float(value),
        DraftMatchPattern::String(value) => E::String(value),
        DraftMatchPattern::Bool(value) => E::Bool(value),
        DraftMatchPattern::Nil => E::Nil,
        DraftMatchPattern::Tuple(elements) => E::Tuple(
            elements
                .into_iter()
                .map(|element| freeze(element, values))
                .collect::<Vec<_>>()
                .into_boxed_slice(),
        ),
        DraftMatchPattern::List { elements, tail } => {
            E::List(execution::graph::MatchPatternList::new(
                elements
                    .into_iter()
                    .map(|element| freeze(element, values))
                    .collect(),
                tail.map(freeze_list_tail),
            ))
        }
        DraftMatchPattern::BitArray(pattern) => E::BitArray(freeze_bit_array(pattern, values)),
        DraftMatchPattern::Custom {
            constructor,
            fields,
        } => E::Custom {
            constructor,
            fields: fields
                .into_iter()
                .map(|field| freeze(field, values))
                .collect::<Vec<_>>()
                .into_boxed_slice(),
        },
        DraftMatchPattern::StringPrefix {
            prefix,
            left,
            right,
        } => E::StringPrefix {
            prefix,
            left: left.map(freeze_binding),
            right: right.map(freeze_binding),
        },
        DraftMatchPattern::Alias { pattern, binding } => E::Alias {
            pattern: Box::new(freeze(*pattern, values)),
            binding: freeze_binding(binding),
        },
    }
}

fn freeze_binding(binding: DraftMatchPatternBinding) -> execution::graph::MatchPatternBinding {
    execution::graph::MatchPatternBinding::new(binding.index)
}

fn freeze_list_tail(tail: DraftMatchListTail) -> execution::graph::MatchPatternListTail {
    match tail {
        DraftMatchListTail::Ignore => execution::graph::MatchPatternListTail::Ignore,
        DraftMatchListTail::Bind(binding) => {
            execution::graph::MatchPatternListTail::Bind(freeze_binding(binding))
        }
    }
}

fn freeze_bit_array(
    pattern: DraftBitArrayPattern,
    values: &BlockValues,
) -> execution::graph::BitArrayPattern {
    execution::graph::BitArrayPattern::new(
        pattern
            .segments
            .into_iter()
            .map(|segment| freeze_bit_array_segment(segment, values))
            .collect(),
    )
}

fn freeze_bit_array_segment(
    segment: DraftBitArrayPatternSegment,
    values: &BlockValues,
) -> execution::graph::BitArrayPatternSegment {
    use execution::graph::BitArrayPatternSegment as E;

    match segment {
        DraftBitArrayPatternSegment::Int {
            pattern,
            size,
            endianness,
            signedness,
        } => E::Int {
            pattern: freeze_bit_array_value(pattern),
            size: freeze_bit_array_size(size, values),
            endianness,
            signedness,
        },
        DraftBitArrayPatternSegment::Float {
            pattern,
            size,
            endianness,
        } => E::Float {
            pattern: freeze_bit_array_value(pattern),
            size: freeze_bit_array_size(size, values),
            endianness,
        },
        DraftBitArrayPatternSegment::Bits {
            pattern,
            size,
            unit,
        } => E::Bits {
            pattern: freeze_bit_array_binding(pattern),
            size: size.map(|size| freeze_bit_array_size(size, values)),
            unit,
        },
        DraftBitArrayPatternSegment::String { pattern, encoding } => E::String {
            pattern: match pattern {
                DraftBitArrayStringPattern::Literal(value) => {
                    execution::graph::BitArrayStringPattern::Literal(value)
                }
                DraftBitArrayStringPattern::Discard => {
                    execution::graph::BitArrayStringPattern::Discard
                }
            },
            encoding,
        },
        DraftBitArrayPatternSegment::UtfCodepoint { pattern, encoding } => E::UtfCodepoint {
            pattern: freeze_bit_array_binding(pattern),
            encoding,
        },
    }
}

fn freeze_bit_array_size(
    size: DraftBitArrayPatternSize,
    values: &BlockValues,
) -> execution::graph::BitArrayPatternSize {
    execution::graph::BitArrayPatternSize::new(
        freeze_bit_array_size_expr(size.value, values),
        size.unit,
    )
}

fn freeze_bit_array_size_expr(
    expression: DraftBitArrayPatternSizeExpr,
    values: &BlockValues,
) -> execution::graph::BitArrayPatternSizeExpr {
    use execution::graph::BitArrayPatternSizeExpr as E;

    match expression {
        DraftBitArrayPatternSizeExpr::Value(value) => E::Value(value),
        DraftBitArrayPatternSizeExpr::Local(value) => E::Local(values.int(&value)),
        DraftBitArrayPatternSizeExpr::Binding(index) => {
            E::Binding(execution::graph::MatchIntBindingId::new(index))
        }
        DraftBitArrayPatternSizeExpr::Add { left, right } => E::Add {
            left: Box::new(freeze_bit_array_size_expr(*left, values)),
            right: Box::new(freeze_bit_array_size_expr(*right, values)),
        },
        DraftBitArrayPatternSizeExpr::Subtract { left, right } => E::Subtract {
            left: Box::new(freeze_bit_array_size_expr(*left, values)),
            right: Box::new(freeze_bit_array_size_expr(*right, values)),
        },
        DraftBitArrayPatternSizeExpr::Multiply { left, right } => E::Multiply {
            left: Box::new(freeze_bit_array_size_expr(*left, values)),
            right: Box::new(freeze_bit_array_size_expr(*right, values)),
        },
        DraftBitArrayPatternSizeExpr::Divide { left, right } => E::Divide {
            left: Box::new(freeze_bit_array_size_expr(*left, values)),
            right: Box::new(freeze_bit_array_size_expr(*right, values)),
        },
        DraftBitArrayPatternSizeExpr::Remainder { left, right } => E::Remainder {
            left: Box::new(freeze_bit_array_size_expr(*left, values)),
            right: Box::new(freeze_bit_array_size_expr(*right, values)),
        },
    }
}

fn freeze_bit_array_value<Value>(
    pattern: DraftBitArrayPatternValue<Value>,
) -> execution::graph::BitArrayPatternValue<Value> {
    match pattern {
        DraftBitArrayPatternValue::Literal(value) => {
            execution::graph::BitArrayPatternValue::Literal(value)
        }
        DraftBitArrayPatternValue::Bind(binding) => {
            execution::graph::BitArrayPatternValue::Bind(freeze_binding(binding))
        }
        DraftBitArrayPatternValue::Discard => execution::graph::BitArrayPatternValue::Discard,
        DraftBitArrayPatternValue::Alias { pattern, binding } => {
            execution::graph::BitArrayPatternValue::Alias {
                pattern: Box::new(freeze_bit_array_value(*pattern)),
                binding: freeze_binding(binding),
            }
        }
    }
}

fn freeze_bit_array_binding(
    pattern: DraftBitArrayBindingPattern,
) -> execution::graph::BitArrayBindingPattern {
    match pattern {
        DraftBitArrayBindingPattern::Bind(binding) => {
            execution::graph::BitArrayBindingPattern::Bind(freeze_binding(binding))
        }
        DraftBitArrayBindingPattern::Discard => execution::graph::BitArrayBindingPattern::Discard,
        DraftBitArrayBindingPattern::Alias { pattern, binding } => {
            execution::graph::BitArrayBindingPattern::Alias {
                pattern: Box::new(freeze_bit_array_binding(*pattern)),
                binding: freeze_binding(binding),
            }
        }
    }
}