Feature: Grackle stores array length in KCEP
When Grackle compiles a KCL array into KCEP memory, it will write the array length as the first element in memory.
This commit is contained in:
		@ -17,7 +17,12 @@ 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 {
 | 
			
		||||
        /// Address where the length of the array is stored.
 | 
			
		||||
        length_at: Address,
 | 
			
		||||
        /// Where is each element in the array bound?
 | 
			
		||||
        elements: Vec<EpBinding>,
 | 
			
		||||
    },
 | 
			
		||||
    /// A sequence of KCL values, indexed by their identifier.
 | 
			
		||||
    Map(HashMap<String, EpBinding>),
 | 
			
		||||
    /// Not associated with a KCEP address.
 | 
			
		||||
@ -38,7 +43,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 { length_at, elements } => {
 | 
			
		||||
                        let i = usize::try_from(i).map_err(|_| CompileError::InvalidIndex(i.to_string()))?;
 | 
			
		||||
                        elements
 | 
			
		||||
                            .get(i)
 | 
			
		||||
 | 
			
		||||
@ -431,9 +431,14 @@ impl Planner {
 | 
			
		||||
                Ok((instructions, binding))
 | 
			
		||||
            }
 | 
			
		||||
            KclValueGroup::ArrayExpression(expr) => {
 | 
			
		||||
                let length_at = self.next_addr.offset_by(1);
 | 
			
		||||
                let mut instructions = vec![Instruction::SetPrimitive {
 | 
			
		||||
                    address: length_at,
 | 
			
		||||
                    value: expr.elements.len().into(),
 | 
			
		||||
                }];
 | 
			
		||||
                // 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 (instrs, bindings) = expr.elements.into_iter().try_fold(
 | 
			
		||||
                    (Vec::new(), Vec::new()),
 | 
			
		||||
                    |(mut acc_instrs, mut acc_bindings), element| {
 | 
			
		||||
                        match KclValueGroup::from(element) {
 | 
			
		||||
@ -449,6 +454,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 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()
 | 
			
		||||
@ -458,7 +468,7 @@ impl Planner {
 | 
			
		||||
                                        seq.push(binding);
 | 
			
		||||
                                        Ok(seq)
 | 
			
		||||
                                    })
 | 
			
		||||
                                    .map(|elements| EpBinding::Sequence { elements })?;
 | 
			
		||||
                                    .map(|elements| EpBinding::Sequence { length_at, elements })?;
 | 
			
		||||
                                acc_bindings.push(binding);
 | 
			
		||||
                            }
 | 
			
		||||
                            KclValueGroup::ObjectExpression(expr) => {
 | 
			
		||||
@ -483,7 +493,14 @@ impl Planner {
 | 
			
		||||
                        Ok((acc_instrs, acc_bindings))
 | 
			
		||||
                    },
 | 
			
		||||
                )?;
 | 
			
		||||
                Ok((instructions, EpBinding::Sequence { elements: bindings }))
 | 
			
		||||
                instructions.extend(instrs);
 | 
			
		||||
                Ok((
 | 
			
		||||
                    instructions,
 | 
			
		||||
                    EpBinding::Sequence {
 | 
			
		||||
                        length_at,
 | 
			
		||||
                        elements: bindings,
 | 
			
		||||
                    },
 | 
			
		||||
                ))
 | 
			
		||||
            }
 | 
			
		||||
            KclValueGroup::ObjectExpression(expr) => {
 | 
			
		||||
                // Convert the object to a sequence of key-value pairs.
 | 
			
		||||
@ -502,6 +519,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: n.into(),
 | 
			
		||||
                                });
 | 
			
		||||
                                let binding = expr
 | 
			
		||||
                                    .elements
 | 
			
		||||
                                    .into_iter()
 | 
			
		||||
@ -511,7 +533,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) => {
 | 
			
		||||
 | 
			
		||||
@ -48,16 +48,22 @@ fn bind_array() {
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        plan,
 | 
			
		||||
        vec![
 | 
			
		||||
            // Arrays start with the length.
 | 
			
		||||
            Instruction::SetPrimitive {
 | 
			
		||||
                address: Address::ZERO,
 | 
			
		||||
                value: 3usize.into(),
 | 
			
		||||
            },
 | 
			
		||||
            // Then the elements follow.
 | 
			
		||||
            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(),
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
@ -73,14 +79,22 @@ fn bind_nested_array() {
 | 
			
		||||
        vec![
 | 
			
		||||
            Instruction::SetPrimitive {
 | 
			
		||||
                address: Address::ZERO,
 | 
			
		||||
                value: 2usize.into(),
 | 
			
		||||
            },
 | 
			
		||||
            Instruction::SetPrimitive {
 | 
			
		||||
                address: Address::ZERO + 1,
 | 
			
		||||
                value: 44i64.into(),
 | 
			
		||||
            },
 | 
			
		||||
            Instruction::SetPrimitive {
 | 
			
		||||
                address: Address::ZERO.offset(1),
 | 
			
		||||
                address: Address::ZERO + 2,
 | 
			
		||||
                value: 2usize.into(),
 | 
			
		||||
            },
 | 
			
		||||
            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(),
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
@ -96,14 +110,18 @@ fn bind_arrays_with_objects_elements() {
 | 
			
		||||
        vec![
 | 
			
		||||
            Instruction::SetPrimitive {
 | 
			
		||||
                address: Address::ZERO,
 | 
			
		||||
                value: 2usize.into()
 | 
			
		||||
            },
 | 
			
		||||
            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(),
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
@ -272,7 +290,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:?}");
 | 
			
		||||
@ -280,7 +298,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,7 +722,7 @@ fn store_object() {
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn store_object_with_array_property() {
 | 
			
		||||
    let program = "const x0 = {a: 1, b: [2, 3]}";
 | 
			
		||||
    let program = "const x0 = {a: 1, b: [22, 33]}";
 | 
			
		||||
    let (actual, bindings) = must_plan(program);
 | 
			
		||||
    let expected = vec![
 | 
			
		||||
        Instruction::SetPrimitive {
 | 
			
		||||
@ -712,12 +730,16 @@ fn store_object_with_array_property() {
 | 
			
		||||
            value: 1i64.into(),
 | 
			
		||||
        },
 | 
			
		||||
        Instruction::SetPrimitive {
 | 
			
		||||
            address: Address::ZERO.offset(1),
 | 
			
		||||
            value: 2i64.into(),
 | 
			
		||||
            address: Address::ZERO + 1,
 | 
			
		||||
            value: 2usize.into(),
 | 
			
		||||
        },
 | 
			
		||||
        Instruction::SetPrimitive {
 | 
			
		||||
            address: Address::ZERO.offset(2),
 | 
			
		||||
            value: 3i64.into(),
 | 
			
		||||
            address: Address::ZERO + 2,
 | 
			
		||||
            value: 22i64.into(),
 | 
			
		||||
        },
 | 
			
		||||
        Instruction::SetPrimitive {
 | 
			
		||||
            address: Address::ZERO + 3,
 | 
			
		||||
            value: 33i64.into(),
 | 
			
		||||
        },
 | 
			
		||||
    ];
 | 
			
		||||
    assert_eq!(actual, expected);
 | 
			
		||||
@ -729,9 +751,10 @@ fn store_object_with_array_property() {
 | 
			
		||||
            (
 | 
			
		||||
                "b".to_owned(),
 | 
			
		||||
                EpBinding::Sequence {
 | 
			
		||||
                    length_at: Address::ZERO.offset(1),
 | 
			
		||||
                    elements: vec![
 | 
			
		||||
                        EpBinding::Single(Address::ZERO.offset(1)),
 | 
			
		||||
                        EpBinding::Single(Address::ZERO.offset(2)),
 | 
			
		||||
                        EpBinding::Single(Address::ZERO.offset(3)),
 | 
			
		||||
                    ]
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user