Grackle: Write array length before array (#1326)
This gives the Execution Plan virtual machine the information it needs to look up indices of arrays at runtime.
This commit is contained in:
		@ -17,7 +17,10 @@ pub enum EpBinding {
 | 
			
		||||
    /// A KCL value which gets stored in a particular address in KCEP memory.
 | 
			
		||||
    Single(Address),
 | 
			
		||||
    /// A sequence of KCL values, indexed by their position in the sequence.
 | 
			
		||||
    Sequence { elements: Vec<EpBinding> },
 | 
			
		||||
    Sequence {
 | 
			
		||||
        length_at: Address,
 | 
			
		||||
        elements: Vec<EpBinding>,
 | 
			
		||||
    },
 | 
			
		||||
    /// A sequence of KCL values, indexed by their identifier.
 | 
			
		||||
    Map(HashMap<String, EpBinding>),
 | 
			
		||||
    /// Not associated with a KCEP address.
 | 
			
		||||
@ -38,7 +41,7 @@ impl EpBinding {
 | 
			
		||||
            LiteralIdentifier::Literal(litval) => match litval.value {
 | 
			
		||||
                // Arrays can be indexed by integers.
 | 
			
		||||
                LiteralValue::IInteger(i) => match self {
 | 
			
		||||
                    EpBinding::Sequence { elements } => {
 | 
			
		||||
                    EpBinding::Sequence { elements, length_at: _ } => {
 | 
			
		||||
                        let i = usize::try_from(i).map_err(|_| CompileError::InvalidIndex(i.to_string()))?;
 | 
			
		||||
                        elements
 | 
			
		||||
                            .get(i)
 | 
			
		||||
 | 
			
		||||
@ -456,6 +456,11 @@ impl Planner {
 | 
			
		||||
                        // each element of that array. Collect their bindings, and bind them all
 | 
			
		||||
                        // under one property of the parent object.
 | 
			
		||||
                        let n = expr.elements.len();
 | 
			
		||||
                        let length_at = self.next_addr.offset_by(1);
 | 
			
		||||
                        acc_instrs.push(Instruction::SetPrimitive {
 | 
			
		||||
                            address: length_at,
 | 
			
		||||
                            value: expr.elements.len().into(),
 | 
			
		||||
                        });
 | 
			
		||||
                        let binding = expr
 | 
			
		||||
                            .elements
 | 
			
		||||
                            .into_iter()
 | 
			
		||||
@ -465,7 +470,7 @@ impl Planner {
 | 
			
		||||
                                acc_instrs.extend(instructions);
 | 
			
		||||
                                Ok(seq)
 | 
			
		||||
                            })
 | 
			
		||||
                            .map(|elements| EpBinding::Sequence { elements })?;
 | 
			
		||||
                            .map(|elements| EpBinding::Sequence { length_at, elements })?;
 | 
			
		||||
                        acc_bindings.insert(key.name, binding);
 | 
			
		||||
                    }
 | 
			
		||||
                    KclValueGroup::ObjectExpression(expr) => {
 | 
			
		||||
@ -503,7 +508,12 @@ impl Planner {
 | 
			
		||||
    ) -> Result<EvalPlan, CompileError> {
 | 
			
		||||
        // First, emit a plan to compute each element of the array.
 | 
			
		||||
        // Collect all the bindings from each element too.
 | 
			
		||||
        let (instructions, bindings) = expr.elements.into_iter().try_fold(
 | 
			
		||||
        let length_at = self.next_addr.offset_by(1);
 | 
			
		||||
        let mut instructions = vec![Instruction::SetPrimitive {
 | 
			
		||||
            address: length_at,
 | 
			
		||||
            value: expr.elements.len().into(),
 | 
			
		||||
        }];
 | 
			
		||||
        let (instrs, bindings) = expr.elements.into_iter().try_fold(
 | 
			
		||||
            (Vec::new(), Vec::new()),
 | 
			
		||||
            |(mut acc_instrs, mut acc_bindings), element| {
 | 
			
		||||
                match KclValueGroup::from(element) {
 | 
			
		||||
@ -519,6 +529,11 @@ impl Planner {
 | 
			
		||||
                        // emit a plan to calculate each element of this child array.
 | 
			
		||||
                        // Then we collect the child array's bindings, and bind them to one
 | 
			
		||||
                        // element of the parent array.
 | 
			
		||||
                        let sublength_at = self.next_addr.offset_by(1);
 | 
			
		||||
                        acc_instrs.push(Instruction::SetPrimitive {
 | 
			
		||||
                            address: sublength_at,
 | 
			
		||||
                            value: expr.elements.len().into(),
 | 
			
		||||
                        });
 | 
			
		||||
                        let binding = expr
 | 
			
		||||
                            .elements
 | 
			
		||||
                            .into_iter()
 | 
			
		||||
@ -528,7 +543,10 @@ impl Planner {
 | 
			
		||||
                                seq.push(binding);
 | 
			
		||||
                                Ok(seq)
 | 
			
		||||
                            })
 | 
			
		||||
                            .map(|elements| EpBinding::Sequence { elements })?;
 | 
			
		||||
                            .map(|elements| EpBinding::Sequence {
 | 
			
		||||
                                length_at: sublength_at,
 | 
			
		||||
                                elements,
 | 
			
		||||
                            })?;
 | 
			
		||||
                        acc_bindings.push(binding);
 | 
			
		||||
                    }
 | 
			
		||||
                    KclValueGroup::ObjectExpression(expr) => {
 | 
			
		||||
@ -553,9 +571,13 @@ impl Planner {
 | 
			
		||||
                Ok((acc_instrs, acc_bindings))
 | 
			
		||||
            },
 | 
			
		||||
        )?;
 | 
			
		||||
        instructions.extend(instrs);
 | 
			
		||||
        Ok(EvalPlan {
 | 
			
		||||
            instructions,
 | 
			
		||||
            binding: EpBinding::Sequence { elements: bindings },
 | 
			
		||||
            binding: EpBinding::Sequence {
 | 
			
		||||
                length_at,
 | 
			
		||||
                elements: bindings,
 | 
			
		||||
            },
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -48,16 +48,22 @@ fn bind_array() {
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        plan,
 | 
			
		||||
        vec![
 | 
			
		||||
            // Array length
 | 
			
		||||
            Instruction::SetPrimitive {
 | 
			
		||||
                address: Address::ZERO,
 | 
			
		||||
                value: 3usize.into()
 | 
			
		||||
            },
 | 
			
		||||
            // Array contents
 | 
			
		||||
            Instruction::SetPrimitive {
 | 
			
		||||
                address: Address::ZERO + 1,
 | 
			
		||||
                value: 44i64.into(),
 | 
			
		||||
            },
 | 
			
		||||
            Instruction::SetPrimitive {
 | 
			
		||||
                address: Address::ZERO.offset(1),
 | 
			
		||||
                address: Address::ZERO + 2,
 | 
			
		||||
                value: 55i64.into(),
 | 
			
		||||
            },
 | 
			
		||||
            Instruction::SetPrimitive {
 | 
			
		||||
                address: Address::ZERO.offset(2),
 | 
			
		||||
                address: Address::ZERO + 3,
 | 
			
		||||
                value: "sixty-six".to_owned().into(),
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
@ -71,16 +77,28 @@ fn bind_nested_array() {
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        plan,
 | 
			
		||||
        vec![
 | 
			
		||||
            // Outer array length
 | 
			
		||||
            Instruction::SetPrimitive {
 | 
			
		||||
                address: Address::ZERO,
 | 
			
		||||
                value: 2usize.into(),
 | 
			
		||||
            },
 | 
			
		||||
            // Outer array contents
 | 
			
		||||
            Instruction::SetPrimitive {
 | 
			
		||||
                address: Address::ZERO + 1,
 | 
			
		||||
                value: 44i64.into(),
 | 
			
		||||
            },
 | 
			
		||||
            // Inner array length
 | 
			
		||||
            Instruction::SetPrimitive {
 | 
			
		||||
                address: Address::ZERO.offset(1),
 | 
			
		||||
                address: Address::ZERO + 2,
 | 
			
		||||
                value: 2usize.into(),
 | 
			
		||||
            },
 | 
			
		||||
            // Inner array length
 | 
			
		||||
            Instruction::SetPrimitive {
 | 
			
		||||
                address: Address::ZERO + 3,
 | 
			
		||||
                value: 55i64.into(),
 | 
			
		||||
            },
 | 
			
		||||
            Instruction::SetPrimitive {
 | 
			
		||||
                address: Address::ZERO.offset(2),
 | 
			
		||||
                address: Address::ZERO + 4,
 | 
			
		||||
                value: "sixty-six".to_owned().into(),
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
@ -94,16 +112,22 @@ fn bind_arrays_with_objects_elements() {
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        plan,
 | 
			
		||||
        vec![
 | 
			
		||||
            // Array length
 | 
			
		||||
            Instruction::SetPrimitive {
 | 
			
		||||
                address: Address::ZERO,
 | 
			
		||||
                value: 2usize.into()
 | 
			
		||||
            },
 | 
			
		||||
            // Array contents
 | 
			
		||||
            Instruction::SetPrimitive {
 | 
			
		||||
                address: Address::ZERO + 1,
 | 
			
		||||
                value: 44i64.into(),
 | 
			
		||||
            },
 | 
			
		||||
            Instruction::SetPrimitive {
 | 
			
		||||
                address: Address::ZERO.offset(1),
 | 
			
		||||
                address: Address::ZERO + 2,
 | 
			
		||||
                value: 55i64.into(),
 | 
			
		||||
            },
 | 
			
		||||
            Instruction::SetPrimitive {
 | 
			
		||||
                address: Address::ZERO.offset(2),
 | 
			
		||||
                address: Address::ZERO + 3,
 | 
			
		||||
                value: "sixty-six".to_owned().into(),
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
@ -265,7 +289,7 @@ fn member_expressions_array() {
 | 
			
		||||
    let (_plan, scope) = must_plan(program);
 | 
			
		||||
    match scope.get("first").unwrap() {
 | 
			
		||||
        EpBinding::Single(addr) => {
 | 
			
		||||
            assert_eq!(*addr, Address::ZERO);
 | 
			
		||||
            assert_eq!(*addr, Address::ZERO + 2);
 | 
			
		||||
        }
 | 
			
		||||
        other => {
 | 
			
		||||
            panic!("expected 'number' bound to 0x0 but it was bound to {other:?}");
 | 
			
		||||
@ -273,7 +297,7 @@ fn member_expressions_array() {
 | 
			
		||||
    }
 | 
			
		||||
    match scope.get("last").unwrap() {
 | 
			
		||||
        EpBinding::Single(addr) => {
 | 
			
		||||
            assert_eq!(*addr, Address::ZERO + 3);
 | 
			
		||||
            assert_eq!(*addr, Address::ZERO + 6);
 | 
			
		||||
        }
 | 
			
		||||
        other => {
 | 
			
		||||
            panic!("expected 'number' bound to 0x3 but it was bound to {other:?}");
 | 
			
		||||
@ -704,17 +728,21 @@ fn store_object_with_array_property() {
 | 
			
		||||
            address: Address::ZERO,
 | 
			
		||||
            value: 1i64.into(),
 | 
			
		||||
        },
 | 
			
		||||
        // Array length
 | 
			
		||||
        Instruction::SetPrimitive {
 | 
			
		||||
            address: Address::ZERO.offset(1),
 | 
			
		||||
            address: Address::ZERO + 1,
 | 
			
		||||
            value: 2usize.into(),
 | 
			
		||||
        },
 | 
			
		||||
        Instruction::SetPrimitive {
 | 
			
		||||
            address: Address::ZERO + 2,
 | 
			
		||||
            value: 2i64.into(),
 | 
			
		||||
        },
 | 
			
		||||
        Instruction::SetPrimitive {
 | 
			
		||||
            address: Address::ZERO.offset(2),
 | 
			
		||||
            address: Address::ZERO + 3,
 | 
			
		||||
            value: 3i64.into(),
 | 
			
		||||
        },
 | 
			
		||||
    ];
 | 
			
		||||
    assert_eq!(actual, expected);
 | 
			
		||||
    eprintln!("{bindings:#?}");
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        bindings.get("x0").unwrap(),
 | 
			
		||||
        &EpBinding::Map(HashMap::from([
 | 
			
		||||
@ -722,9 +750,10 @@ fn store_object_with_array_property() {
 | 
			
		||||
            (
 | 
			
		||||
                "b".to_owned(),
 | 
			
		||||
                EpBinding::Sequence {
 | 
			
		||||
                    length_at: Address::ZERO + 1,
 | 
			
		||||
                    elements: vec![
 | 
			
		||||
                        EpBinding::Single(Address::ZERO.offset(1)),
 | 
			
		||||
                        EpBinding::Single(Address::ZERO.offset(2)),
 | 
			
		||||
                        EpBinding::Single(Address::ZERO + 2),
 | 
			
		||||
                        EpBinding::Single(Address::ZERO + 3),
 | 
			
		||||
                    ]
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
@ -771,17 +800,22 @@ fn arrays_as_parameters() {
 | 
			
		||||
    let array = identity([1,2,3])";
 | 
			
		||||
    let (plan, scope) = must_plan(program);
 | 
			
		||||
    let expected_plan = vec![
 | 
			
		||||
        // Array contents
 | 
			
		||||
        // Array length
 | 
			
		||||
        Instruction::SetPrimitive {
 | 
			
		||||
            address: Address::ZERO,
 | 
			
		||||
            value: 3usize.into(),
 | 
			
		||||
        },
 | 
			
		||||
        // Array contents
 | 
			
		||||
        Instruction::SetPrimitive {
 | 
			
		||||
            address: Address::ZERO + 1,
 | 
			
		||||
            value: 1i64.into(),
 | 
			
		||||
        },
 | 
			
		||||
        Instruction::SetPrimitive {
 | 
			
		||||
            address: Address::ZERO + 1,
 | 
			
		||||
            address: Address::ZERO + 2,
 | 
			
		||||
            value: 2i64.into(),
 | 
			
		||||
        },
 | 
			
		||||
        Instruction::SetPrimitive {
 | 
			
		||||
            address: Address::ZERO + 2,
 | 
			
		||||
            address: Address::ZERO + 3,
 | 
			
		||||
            value: 3i64.into(),
 | 
			
		||||
        },
 | 
			
		||||
    ];
 | 
			
		||||
@ -789,10 +823,11 @@ fn arrays_as_parameters() {
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        scope.get("array").unwrap(),
 | 
			
		||||
        &EpBinding::Sequence {
 | 
			
		||||
            length_at: Address::ZERO,
 | 
			
		||||
            elements: vec![
 | 
			
		||||
                EpBinding::Single(Address::ZERO),
 | 
			
		||||
                EpBinding::Single(Address::ZERO + 1),
 | 
			
		||||
                EpBinding::Single(Address::ZERO + 2),
 | 
			
		||||
                EpBinding::Single(Address::ZERO + 3),
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user