fn compile_if_with_monad_conditional(bindings: SymbolBindings, conditional: CellRef, if_true: CellRef, if_false: CellRef, if_true_reftype: ReferenceType, if_false_reftype: ReferenceType) -> Result<(SyntaxCompiler, SymbolBindings), (BindError, SymbolBindings)> {

    // Rebind the if_true and if_false values into a new frame
    let mut interior_bindings   = bindings.push_new_frame();
    let mut mapped_bindings     = HashMap::new();
    let condition_cell          = interior_bindings.alloc_cell();

    let mut map_interior_cell   = |FrameReference(cell_id, frame_depth, ref_type)| {
        if frame_depth == 0 {

            if let Some(new_cell_id) = mapped_bindings.get(&cell_id) {
                // Cell has already been remapped
                Some(SafasCell::FrameReference(*new_cell_id, frame_depth, ref_type).into())
            } else {
                // Reallocate this reference and import from outside
                let new_cell_id = interior_bindings.alloc_cell();

                mapped_bindings.insert(cell_id, new_cell_id);
                interior_bindings.import(SafasCell::FrameReference(cell_id, frame_depth, ref_type).into(), new_cell_id);

                Some(SafasCell::FrameReference(new_cell_id, frame_depth, ref_type).into())
            }
            
        } else {
            // Not in the current frame
            None
        }
    };
    let if_true_inner           = substitute_frame_refs(if_true, &mut map_interior_cell);
    let if_false_inner          = substitute_frame_refs(if_false, &mut map_interior_cell);

    // Done with the interior bindings
    let num_cells_for_closure   = interior_bindings.num_cells;
    let (bindings, imports)     = interior_bindings.pop();

    // Compile the if_true and if_false statements (in the interior frame so we can call them as a closure)
    let if_true_inner           = compile_several_statements(if_true_inner);
    let if_false_inner          = compile_several_statements(if_false_inner);

    let mut if_true_inner       = match if_true_inner { Ok(result) => result, Err(err) => return Err((err, bindings)) };
    let mut if_false_inner      = match if_false_inner { Ok(result) => result, Err(err) => return Err((err, bindings)) };

    // Build up the flat_map function code
    let mut flat_map_if         = CompiledActions::empty();

    // If true and if false wrap their result if they don't already return a monad
    if if_true_reftype != ReferenceType::Monad  { if_true_inner.push(Action::Wrap) }
    if if_false_reftype != ReferenceType::Monad { if_false_inner.push(Action::Wrap) }

    // If_true branches over if_false once it has finished executing
    if_true_inner.push(Action::Jump((if_false_inner.actions.len()+1) as isize));

    // Load the condition and branch to if_false if needed
    flat_map_if.push(Action::CellValue(condition_cell));
    flat_map_if.push(Action::JumpIfFalse((if_true_inner.actions.len()+1) as isize));

    // Perform the two interior actions
    flat_map_if.extend(if_true_inner);
    flat_map_if.extend(if_false_inner);

    // Turn into a stack closure
    let flat_map_if = flat_map_if.to_actions().collect::<Vec<_>>();
    let closure     = StackClosure::new(flat_map_if, imports.iter().map(|(_val, cell)| *cell), num_cells_for_closure, 1, true);
    let closure     = CellRef::new(SafasCell::FrameMonad(Box::new(closure)));

    // Turn the imports into the values we'll need to push to the stack (these are the values we pass into the compiler)
    let imports     = imports.into_iter().map(|(val, _)| val);
    let imports     = SafasCell::list_with_cells(imports);
    let imports_and_condition = SafasCell::list_with_cells(vec![conditional, imports]);

    // Build the final compiler
    let compiler    = move |imports_and_condition: CellRef| {
        // Parameters are the conditional statements and the closure import list
        let ListTuple((conditional, imports)): ListTuple<(CellRef, CellRef)> = imports_and_condition.try_into()?;

        // Evaluate the condition monad
        let mut result = compile_several_statements(conditional)?;

        // Push onto the stack for our flat_map later
        result.push(Action::Push);

        // Push the imports onto the stack
        let mut pos = &*imports;
        while let SafasCell::List(import, next) = pos {
            let import = compile_statement(import.clone())?;
            result.extend(import);
            result.push(Action::Push);

            pos = next;
        }

        // Load the closure and capture the imports
        result.push(Action::Value(closure.clone()));
        result.push(Action::Call);

        // FlatMap with the condition
        result.push(Action::FlatMap);

        Ok(result)
    };

    // Final result
    let compiler = SyntaxCompiler::with_compiler_and_reftype(compiler, imports_and_condition, ReferenceType::Monad);
    Ok((compiler, bindings))
}
///
/// Given an if statement where the conditional evaluates to a monad function, writes out a flat_map function 
///
Given an if statement where the conditional evaluates to a monad function, writes out a flat_map function
Given an if statement where the conditional evaluates to a monad function writes out a flat_map function
fn compile_if_with_monad_conditional(bindings: SymbolBindings, conditional: CellRef, if_true: CellRef, if_false: CellRef, if_true_reftype: ReferenceType, if_false_reftype: ReferenceType) -> Result<(SyntaxCompiler, SymbolBindings), (BindError, SymbolBindings)> {
    // Rebind the if_true and if_false values into a new frame
    let mut interior_bindings   = bindings.push_new_frame();
    let mut mapped_bindings     = HashMap::new();
    let condition_cell          = interior_bindings.alloc_cell();
    let mut map_interior_cell   = |FrameReference(cell_id, frame_depth, ref_type)| {
        if frame_depth == 0 {
            if let Some(new_cell_id) = mapped_bindings.get(&cell_id) {
                // Cell has already been remapped
                Some(SafasCell::FrameReference(*new_cell_id, frame_depth, ref_type).into())
            } else {
                // Reallocate this reference and import from outside
                let new_cell_id = interior_bindings.alloc_cell();
                mapped_bindings.insert(cell_id, new_cell_id);
                interior_bindings.import(SafasCell::FrameReference(cell_id, frame_depth, ref_type).into(), new_cell_id);
                Some(SafasCell::FrameReference(new_cell_id, frame_depth, ref_type).into())
            }
        } else {
            // Not in the current frame
            None
        }
    };
    let if_true_inner           = substitute_frame_refs(if_true, &mut map_interior_cell);
    let if_false_inner          = substitute_frame_refs(if_false, &mut map_interior_cell);
    // Done with the interior bindings
    let num_cells_for_closure   = interior_bindings.num_cells;
    let (bindings, imports)     = interior_bindings.pop();
    // Compile the if_true and if_false statements (in the interior frame so we can call them as a closure)
    let if_true_inner           = compile_several_statements(if_true_inner);
    let if_false_inner          = compile_several_statements(if_false_inner);
    let mut if_true_inner       = match if_true_inner { Ok(result) => result, Err(err) => return Err((err, bindings)) };
    let mut if_false_inner      = match if_false_inner { Ok(result) => result, Err(err) => return Err((err, bindings)) };
    // Build up the flat_map function code
    let mut flat_map_if         = CompiledActions::empty();
    // If true and if false wrap their result if they don't already return a monad
    if if_true_reftype != ReferenceType::Monad  { if_true_inner.push(Action::Wrap) }
    if if_false_reftype != ReferenceType::Monad { if_false_inner.push(Action::Wrap) }
    // If_true branches over if_false once it has finished executing
    if_true_inner.push(Action::Jump((if_false_inner.actions.len()+1) as isize));
    // Load the condition and branch to if_false if needed
    flat_map_if.push(Action::CellValue(condition_cell));
    flat_map_if.push(Action::JumpIfFalse((if_true_inner.actions.len()+1) as isize));
    // Perform the two interior actions
    flat_map_if.extend(if_true_inner);
    flat_map_if.extend(if_false_inner);
    // Turn into a stack closure
    let flat_map_if = flat_map_if.to_actions().collect::<Vec<_>>();
    let closure     = StackClosure::new(flat_map_if, imports.iter().map(|(_val, cell)| *cell), num_cells_for_closure, 1, true);
    let closure     = CellRef::new(SafasCell::FrameMonad(Box::new(closure)));
    // Turn the imports into the values we'll need to push to the stack (these are the values we pass into the compiler)
    let imports     = imports.into_iter().map(|(val, _)| val);
    let imports     = SafasCell::list_with_cells(imports);
    let imports_and_condition = SafasCell::list_with_cells(vec![conditional, imports]);
    // Build the final compiler
    let compiler    = move |imports_and_condition: CellRef| {
        // Parameters are the conditional statements and the closure import list
        let ListTuple((conditional, imports)): ListTuple<(CellRef, CellRef)> = imports_and_condition.try_into()?;
        // Evaluate the condition monad
        let mut result = compile_several_statements(conditional)?;
        // Push onto the stack for our flat_map later
        result.push(Action::Push);
        // Push the imports onto the stack
        let mut pos = &*imports;
        while let SafasCell::List(import, next) = pos {
            let import = compile_statement(import.clone())?;
            result.extend(import);
            result.push(Action::Push);
            pos = next;
        }
        // Load the closure and capture the imports
        result.push(Action::Value(closure.clone()));
        result.push(Action::Call);
        // FlatMap with the condition
        result.push(Action::FlatMap);
        Ok(result)
    };
    // Final result
    let compiler = SyntaxCompiler::with_compiler_and_reftype(compiler, imports_and_condition, ReferenceType::Monad);
    Ok((compiler, bindings))
}
fn compile_if_with_monad_conditional ( bindings : SymbolBindings , conditional : CellRef , if_true : CellRef , if_false : CellRef , if_true_reftype : ReferenceType , if_false_reftype : ReferenceType ) -> Result < ( SyntaxCompiler , SymbolBindings ) , ( BindError , SymbolBindings ) > { // Rebind the if_true and if_false values into a new frame let mut interior_bindings = bindings . push_new_frame ( ) ; let mut mapped_bindings = HashMap :: new ( ) ; let condition_cell = interior_bindings . alloc_cell ( ) ; let mut map_interior_cell = | FrameReference ( cell_id , frame_depth , ref_type ) | { if frame_depth == 0 { if let Some ( new_cell_id ) = mapped_bindings . get ( & cell_id ) { // Cell has already been remapped Some ( SafasCell :: FrameReference ( * new_cell_id , frame_depth , ref_type ) . into ( ) ) } else { // Reallocate this reference and import from outside let new_cell_id = interior_bindings . alloc_cell ( ) ; mapped_bindings . insert ( cell_id , new_cell_id ) ; interior_bindings . import ( SafasCell :: FrameReference ( cell_id , frame_depth , ref_type ) . into ( ) , new_cell_id ) ; Some ( SafasCell :: FrameReference ( new_cell_id , frame_depth , ref_type ) . into ( ) ) } } else { // Not in the current frame None } } ; let if_true_inner = substitute_frame_refs ( if_true , & mut map_interior_cell ) ; let if_false_inner = substitute_frame_refs ( if_false , & mut map_interior_cell ) ; // Done with the interior bindings let num_cells_for_closure = interior_bindings . num_cells ; let ( bindings , imports ) = interior_bindings . pop ( ) ; // Compile the if_true and if_false statements (in the interior frame so we can call them as a closure) let if_true_inner = compile_several_statements ( if_true_inner ) ; let if_false_inner = compile_several_statements ( if_false_inner ) ; let mut if_true_inner = match if_true_inner { Ok ( result ) => result , Err ( err ) => return Err ( ( err , bindings ) ) } ; let mut if_false_inner = match if_false_inner { Ok ( result ) => result , Err ( err ) => return Err ( ( err , bindings ) ) } ; // Build up the flat_map function code let mut flat_map_if = CompiledActions :: empty ( ) ; // If true and if false wrap their result if they don't already return a monad if if_true_reftype != ReferenceType :: Monad { if_true_inner . push ( Action :: Wrap ) } if if_false_reftype != ReferenceType :: Monad { if_false_inner . push ( Action :: Wrap ) } // If_true branches over if_false once it has finished executing if_true_inner . push ( Action :: Jump ( ( if_false_inner . actions . len ( ) + 1 ) as isize ) ) ; // Load the condition and branch to if_false if needed flat_map_if . push ( Action :: CellValue ( condition_cell ) ) ; flat_map_if . push ( Action :: JumpIfFalse ( ( if_true_inner . actions . len ( ) + 1 ) as isize ) ) ; // Perform the two interior actions flat_map_if . extend ( if_true_inner ) ; flat_map_if . extend ( if_false_inner ) ; // Turn into a stack closure let flat_map_if = flat_map_if . to_actions ( ) . collect :: < Vec < _ > > ( ) ; let closure = StackClosure :: new ( flat_map_if , imports . iter ( ) . map ( | ( _val , cell ) | * cell ) , num_cells_for_closure , 1 , true ) ; let closure = CellRef :: new ( SafasCell :: FrameMonad ( Box :: new ( closure ) ) ) ; // Turn the imports into the values we'll need to push to the stack (these are the values we pass into the compiler) let imports = imports . into_iter ( ) . map ( | ( val , _ ) | val ) ; let imports = SafasCell :: list_with_cells ( imports ) ; let imports_and_condition = SafasCell :: list_with_cells ( vec ! [ conditional imports ] ) ; // Build the final compiler let compiler = move | imports_and_condition : CellRef | { // Parameters are the conditional statements and the closure import list let ListTuple ( ( conditional , imports ) ) : ListTuple < ( CellRef , CellRef ) > = imports_and_condition . try_into ( ) ? ; // Evaluate the condition monad let mut result = compile_several_statements ( conditional ) ? ; // Push onto the stack for our flat_map later result . push ( Action :: Push ) ; // Push the imports onto the stack let mut pos = & * imports ; while let SafasCell :: List ( import , next ) = pos { let import = compile_statement ( import . clone ( ) ) ? ; result . extend ( import ) ; result . push ( Action :: Push ) ; pos = next ; } // Load the closure and capture the imports result . push ( Action :: Value ( closure . clone ( ) ) ) ; result . push ( Action :: Call ) ; // FlatMap with the condition result . push ( Action :: FlatMap ) ; Ok ( result ) } ; // Final result let compiler = SyntaxCompiler :: with_compiler_and_reftype ( compiler , imports_and_condition , ReferenceType :: Monad ) ; Ok ( ( compiler , bindings ) ) }
Given an if statement where the conditional evaluates to a monad function, writes out a flat_map function
Given an if statement where the conditional evaluates to a monad function writes out a flat_map function
bindings
SymbolBindings
conditional
CellRef
if_true
CellRef
if_false
CellRef
if_true_reftype
ReferenceType
if_false_reftype
ReferenceType
bindings
SymbolBindings
conditional
CellRef
if_true
CellRef
if_false
CellRef
if_true_reftype
ReferenceType
if_false_reftype
ReferenceType