fn substitute_cells<SubstituteFn: Fn(usize) -> Option<CellRef>>(bindings: SymbolBindings, allocated_cells: &mut HashMap<usize, usize>, partially_bound: &CellRef, substitutions: &SubstituteFn) -> (CellRef, SymbolBindings) {
    // Bind the cells
    let pos                 = partially_bound;

    match &**pos {
        // Lists are bound recursively
        SafasCell::List(car, cdr) => {
            // TODO: would be more efficient to bind in a loop
            let (car, bindings) = substitute_cells(bindings, allocated_cells, car, substitutions);
            let (cdr, bindings) = substitute_cells(bindings, allocated_cells, cdr, substitutions);

            (SafasCell::List(car, cdr).into(), bindings)
        }

        SafasCell::BoundSyntax(compiler) => {
            let mut bindings    = bindings;
            let substitute      = compiler.substitute_frame_refs(|FrameReference(cell_id, frame, cell_type)| {
                if frame == 0 {
                    // Is from the macro frame: bind via the subtitutions function
                    if let Some(actual_cell) = substitutions(cell_id) {
                        Some(actual_cell)
                    } else {
                        let bound_cell_id = if let Some(bound_cell_id) = allocated_cells.get(&cell_id) {
                            // We've already bound this cell to a value on frame
                            *bound_cell_id
                        } else {
                            // This cell needs to be allocated on the current frame
                            let bound_cell_id = bindings.alloc_cell();
                            allocated_cells.insert(cell_id, bound_cell_id);
                            bound_cell_id
                        };

                        // Return the bound cell
                        Some(SafasCell::FrameReference(bound_cell_id, 0, cell_type).into())
                    }
                } else {
                    // Bound from a different frame
                    None
                }
            });
            (SafasCell::BoundSyntax(substitute).into(), bindings)
        }

        // Frame references are bound by the substitution function
        SafasCell::FrameReference(cell_id, frame, cell_type) => {
            if *frame == 0 {
                // Is from the macro frame: bind via the subtitutions function
                if let Some(actual_cell) = substitutions(*cell_id) {
                    (actual_cell, bindings)
                } else {
                    // Cells that aren't substituted are allocated on the current frame (they should be internal bindings introduced by calls like def)
                    let mut bindings = bindings;

                    let bound_cell_id = if let Some(bound_cell_id) = allocated_cells.get(cell_id) {
                        // We've already bound this cell to a value on frame
                        *bound_cell_id
                    } else {
                        // This cell needs to be allocated on the current frame
                        let bound_cell_id = bindings.alloc_cell();
                        allocated_cells.insert(*cell_id, bound_cell_id);
                        bound_cell_id
                    };

                    // Return the bound cell
                    (SafasCell::FrameReference(bound_cell_id, 0, *cell_type).into(), bindings)
                }
            } else {
                // Bound from a different frame
                (pos.clone(), bindings)
            }
        }

        // Other cell types have no binding to do
        _ => (pos.clone(), bindings)
    }
}
///
/// Substitutes any FrameReferences in the partially bound statement for bound values, and rebinds any FrameReferences that are
/// not currently bound
///
Substitutes any FrameReferences in the partially bound statement for bound values, and rebinds any FrameReferences that are
not currently bound
Substitutes any FrameReferences in the partially bound statement for bound values and rebinds any FrameReferences that are not currently bound
fn substitute_cells<SubstituteFn: Fn(usize) -> Option<CellRef>>(bindings: SymbolBindings, allocated_cells: &mut HashMap<usize, usize>, partially_bound: &CellRef, substitutions: &SubstituteFn) -> (CellRef, SymbolBindings) {
    // Bind the cells
    let pos                 = partially_bound;
    match &**pos {
        // Lists are bound recursively
        SafasCell::List(car, cdr) => {
            // TODO: would be more efficient to bind in a loop
            let (car, bindings) = substitute_cells(bindings, allocated_cells, car, substitutions);
            let (cdr, bindings) = substitute_cells(bindings, allocated_cells, cdr, substitutions);
            (SafasCell::List(car, cdr).into(), bindings)
        }
        SafasCell::BoundSyntax(compiler) => {
            let mut bindings    = bindings;
            let substitute      = compiler.substitute_frame_refs(|FrameReference(cell_id, frame, cell_type)| {
                if frame == 0 {
                    // Is from the macro frame: bind via the subtitutions function
                    if let Some(actual_cell) = substitutions(cell_id) {
                        Some(actual_cell)
                    } else {
                        let bound_cell_id = if let Some(bound_cell_id) = allocated_cells.get(&cell_id) {
                            // We've already bound this cell to a value on frame
                            *bound_cell_id
                        } else {
                            // This cell needs to be allocated on the current frame
                            let bound_cell_id = bindings.alloc_cell();
                            allocated_cells.insert(cell_id, bound_cell_id);
                            bound_cell_id
                        };
                        // Return the bound cell
                        Some(SafasCell::FrameReference(bound_cell_id, 0, cell_type).into())
                    }
                } else {
                    // Bound from a different frame
                    None
                }
            });
            (SafasCell::BoundSyntax(substitute).into(), bindings)
        }
        // Frame references are bound by the substitution function
        SafasCell::FrameReference(cell_id, frame, cell_type) => {
            if *frame == 0 {
                // Is from the macro frame: bind via the subtitutions function
                if let Some(actual_cell) = substitutions(*cell_id) {
                    (actual_cell, bindings)
                } else {
                    // Cells that aren't substituted are allocated on the current frame (they should be internal bindings introduced by calls like def)
                    let mut bindings = bindings;
                    let bound_cell_id = if let Some(bound_cell_id) = allocated_cells.get(cell_id) {
                        // We've already bound this cell to a value on frame
                        *bound_cell_id
                    } else {
                        // This cell needs to be allocated on the current frame
                        let bound_cell_id = bindings.alloc_cell();
                        allocated_cells.insert(*cell_id, bound_cell_id);
                        bound_cell_id
                    };
                    // Return the bound cell
                    (SafasCell::FrameReference(bound_cell_id, 0, *cell_type).into(), bindings)
                }
            } else {
                // Bound from a different frame
                (pos.clone(), bindings)
            }
        }
        // Other cell types have no binding to do
        _ => (pos.clone(), bindings)
    }
}
fn substitute_cells < SubstituteFn : Fn ( usize ) -> Option < CellRef > > ( bindings : SymbolBindings , allocated_cells : & mut HashMap < usize , usize > , partially_bound : & CellRef , substitutions : & SubstituteFn ) -> ( CellRef , SymbolBindings ) { // Bind the cells let pos = partially_bound ; match & * * pos { // Lists are bound recursively SafasCell :: List ( car , cdr ) => { // TODO: would be more efficient to bind in a loop let ( car , bindings ) = substitute_cells ( bindings , allocated_cells , car , substitutions ) ; let ( cdr , bindings ) = substitute_cells ( bindings , allocated_cells , cdr , substitutions ) ; ( SafasCell :: List ( car , cdr ) . into ( ) , bindings ) } SafasCell :: BoundSyntax ( compiler ) => { let mut bindings = bindings ; let substitute = compiler . substitute_frame_refs ( | FrameReference ( cell_id , frame , cell_type ) | { if frame == 0 { // Is from the macro frame: bind via the subtitutions function if let Some ( actual_cell ) = substitutions ( cell_id ) { Some ( actual_cell ) } else { let bound_cell_id = if let Some ( bound_cell_id ) = allocated_cells . get ( & cell_id ) { // We've already bound this cell to a value on frame * bound_cell_id } else { // This cell needs to be allocated on the current frame let bound_cell_id = bindings . alloc_cell ( ) ; allocated_cells . insert ( cell_id , bound_cell_id ) ; bound_cell_id } ; // Return the bound cell Some ( SafasCell :: FrameReference ( bound_cell_id , 0 , cell_type ) . into ( ) ) } } else { // Bound from a different frame None } } ) ; ( SafasCell :: BoundSyntax ( substitute ) . into ( ) , bindings ) } // Frame references are bound by the substitution function SafasCell :: FrameReference ( cell_id , frame , cell_type ) => { if * frame == 0 { // Is from the macro frame: bind via the subtitutions function if let Some ( actual_cell ) = substitutions ( * cell_id ) { ( actual_cell , bindings ) } else { // Cells that aren't substituted are allocated on the current frame (they should be internal bindings introduced by calls like def) let mut bindings = bindings ; let bound_cell_id = if let Some ( bound_cell_id ) = allocated_cells . get ( cell_id ) { // We've already bound this cell to a value on frame * bound_cell_id } else { // This cell needs to be allocated on the current frame let bound_cell_id = bindings . alloc_cell ( ) ; allocated_cells . insert ( * cell_id , bound_cell_id ) ; bound_cell_id } ; // Return the bound cell ( SafasCell :: FrameReference ( bound_cell_id , 0 , * cell_type ) . into ( ) , bindings ) } } else { // Bound from a different frame ( pos . clone ( ) , bindings ) } } // Other cell types have no binding to do _ => ( pos . clone ( ) , bindings ) } }
Substitutes any FrameReferences in the partially bound statement for bound values, and rebinds any FrameReferences that are
not currently bound
Substitutes any FrameReferences in the partially bound statement for bound values and rebinds any FrameReferences that are not currently bound
bindings
SymbolBindings
allocated_cells
&mut HashMap<usize, usize>
partially_bound
&CellRef
substitutions
&SubstituteFn
bindings
SymbolBindings
allocated_cells
&mut HashMap<usize, usize>
partially_bound
&CellRef
substitutions
&SubstituteFn