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.
|
/// A KCL value which gets stored in a particular address in KCEP memory.
|
||||||
Single(Address),
|
Single(Address),
|
||||||
/// A sequence of KCL values, indexed by their position in the sequence.
|
/// 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.
|
/// A sequence of KCL values, indexed by their identifier.
|
||||||
Map(HashMap<String, EpBinding>),
|
Map(HashMap<String, EpBinding>),
|
||||||
/// Not associated with a KCEP address.
|
/// Not associated with a KCEP address.
|
||||||
@ -38,7 +43,7 @@ impl EpBinding {
|
|||||||
LiteralIdentifier::Literal(litval) => match litval.value {
|
LiteralIdentifier::Literal(litval) => match litval.value {
|
||||||
// Arrays can be indexed by integers.
|
// Arrays can be indexed by integers.
|
||||||
LiteralValue::IInteger(i) => match self {
|
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()))?;
|
let i = usize::try_from(i).map_err(|_| CompileError::InvalidIndex(i.to_string()))?;
|
||||||
elements
|
elements
|
||||||
.get(i)
|
.get(i)
|
||||||
|
|||||||
@ -431,9 +431,14 @@ impl Planner {
|
|||||||
Ok((instructions, binding))
|
Ok((instructions, binding))
|
||||||
}
|
}
|
||||||
KclValueGroup::ArrayExpression(expr) => {
|
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.
|
// First, emit a plan to compute each element of the array.
|
||||||
// Collect all the bindings from each element too.
|
// 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()),
|
(Vec::new(), Vec::new()),
|
||||||
|(mut acc_instrs, mut acc_bindings), element| {
|
|(mut acc_instrs, mut acc_bindings), element| {
|
||||||
match KclValueGroup::from(element) {
|
match KclValueGroup::from(element) {
|
||||||
@ -449,6 +454,11 @@ impl Planner {
|
|||||||
// emit a plan to calculate each element of this child array.
|
// emit a plan to calculate each element of this child array.
|
||||||
// Then we collect the child array's bindings, and bind them to one
|
// Then we collect the child array's bindings, and bind them to one
|
||||||
// element of the parent array.
|
// 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
|
let binding = expr
|
||||||
.elements
|
.elements
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -458,7 +468,7 @@ impl Planner {
|
|||||||
seq.push(binding);
|
seq.push(binding);
|
||||||
Ok(seq)
|
Ok(seq)
|
||||||
})
|
})
|
||||||
.map(|elements| EpBinding::Sequence { elements })?;
|
.map(|elements| EpBinding::Sequence { length_at, elements })?;
|
||||||
acc_bindings.push(binding);
|
acc_bindings.push(binding);
|
||||||
}
|
}
|
||||||
KclValueGroup::ObjectExpression(expr) => {
|
KclValueGroup::ObjectExpression(expr) => {
|
||||||
@ -483,7 +493,14 @@ impl Planner {
|
|||||||
Ok((acc_instrs, acc_bindings))
|
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) => {
|
KclValueGroup::ObjectExpression(expr) => {
|
||||||
// Convert the object to a sequence of key-value pairs.
|
// 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
|
// each element of that array. Collect their bindings, and bind them all
|
||||||
// under one property of the parent object.
|
// under one property of the parent object.
|
||||||
let n = expr.elements.len();
|
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
|
let binding = expr
|
||||||
.elements
|
.elements
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -511,7 +533,7 @@ impl Planner {
|
|||||||
acc_instrs.extend(instructions);
|
acc_instrs.extend(instructions);
|
||||||
Ok(seq)
|
Ok(seq)
|
||||||
})
|
})
|
||||||
.map(|elements| EpBinding::Sequence { elements })?;
|
.map(|elements| EpBinding::Sequence { length_at, elements })?;
|
||||||
acc_bindings.insert(key.name, binding);
|
acc_bindings.insert(key.name, binding);
|
||||||
}
|
}
|
||||||
KclValueGroup::ObjectExpression(expr) => {
|
KclValueGroup::ObjectExpression(expr) => {
|
||||||
|
|||||||
@ -48,16 +48,22 @@ fn bind_array() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
plan,
|
plan,
|
||||||
vec![
|
vec![
|
||||||
|
// Arrays start with the length.
|
||||||
Instruction::SetPrimitive {
|
Instruction::SetPrimitive {
|
||||||
address: Address::ZERO,
|
address: Address::ZERO,
|
||||||
|
value: 3usize.into(),
|
||||||
|
},
|
||||||
|
// Then the elements follow.
|
||||||
|
Instruction::SetPrimitive {
|
||||||
|
address: Address::ZERO + 1,
|
||||||
value: 44i64.into(),
|
value: 44i64.into(),
|
||||||
},
|
},
|
||||||
Instruction::SetPrimitive {
|
Instruction::SetPrimitive {
|
||||||
address: Address::ZERO.offset(1),
|
address: Address::ZERO + 2,
|
||||||
value: 55i64.into(),
|
value: 55i64.into(),
|
||||||
},
|
},
|
||||||
Instruction::SetPrimitive {
|
Instruction::SetPrimitive {
|
||||||
address: Address::ZERO.offset(2),
|
address: Address::ZERO + 3,
|
||||||
value: "sixty-six".to_owned().into(),
|
value: "sixty-six".to_owned().into(),
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -73,14 +79,22 @@ fn bind_nested_array() {
|
|||||||
vec![
|
vec![
|
||||||
Instruction::SetPrimitive {
|
Instruction::SetPrimitive {
|
||||||
address: Address::ZERO,
|
address: Address::ZERO,
|
||||||
|
value: 2usize.into(),
|
||||||
|
},
|
||||||
|
Instruction::SetPrimitive {
|
||||||
|
address: Address::ZERO + 1,
|
||||||
value: 44i64.into(),
|
value: 44i64.into(),
|
||||||
},
|
},
|
||||||
Instruction::SetPrimitive {
|
Instruction::SetPrimitive {
|
||||||
address: Address::ZERO.offset(1),
|
address: Address::ZERO + 2,
|
||||||
|
value: 2usize.into(),
|
||||||
|
},
|
||||||
|
Instruction::SetPrimitive {
|
||||||
|
address: Address::ZERO + 3,
|
||||||
value: 55i64.into(),
|
value: 55i64.into(),
|
||||||
},
|
},
|
||||||
Instruction::SetPrimitive {
|
Instruction::SetPrimitive {
|
||||||
address: Address::ZERO.offset(2),
|
address: Address::ZERO + 4,
|
||||||
value: "sixty-six".to_owned().into(),
|
value: "sixty-six".to_owned().into(),
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -96,14 +110,18 @@ fn bind_arrays_with_objects_elements() {
|
|||||||
vec![
|
vec![
|
||||||
Instruction::SetPrimitive {
|
Instruction::SetPrimitive {
|
||||||
address: Address::ZERO,
|
address: Address::ZERO,
|
||||||
|
value: 2usize.into()
|
||||||
|
},
|
||||||
|
Instruction::SetPrimitive {
|
||||||
|
address: Address::ZERO + 1,
|
||||||
value: 44i64.into(),
|
value: 44i64.into(),
|
||||||
},
|
},
|
||||||
Instruction::SetPrimitive {
|
Instruction::SetPrimitive {
|
||||||
address: Address::ZERO.offset(1),
|
address: Address::ZERO + 2,
|
||||||
value: 55i64.into(),
|
value: 55i64.into(),
|
||||||
},
|
},
|
||||||
Instruction::SetPrimitive {
|
Instruction::SetPrimitive {
|
||||||
address: Address::ZERO.offset(2),
|
address: Address::ZERO + 3,
|
||||||
value: "sixty-six".to_owned().into(),
|
value: "sixty-six".to_owned().into(),
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -272,7 +290,7 @@ fn member_expressions_array() {
|
|||||||
let (_plan, scope) = must_plan(program);
|
let (_plan, scope) = must_plan(program);
|
||||||
match scope.get("first").unwrap() {
|
match scope.get("first").unwrap() {
|
||||||
EpBinding::Single(addr) => {
|
EpBinding::Single(addr) => {
|
||||||
assert_eq!(*addr, Address::ZERO);
|
assert_eq!(*addr, Address::ZERO + 2);
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
panic!("expected 'number' bound to 0x0 but it was bound to {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() {
|
match scope.get("last").unwrap() {
|
||||||
EpBinding::Single(addr) => {
|
EpBinding::Single(addr) => {
|
||||||
assert_eq!(*addr, Address::ZERO + 3);
|
assert_eq!(*addr, Address::ZERO + 6);
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
panic!("expected 'number' bound to 0x3 but it was bound to {other:?}");
|
panic!("expected 'number' bound to 0x3 but it was bound to {other:?}");
|
||||||
@ -704,7 +722,7 @@ fn store_object() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn store_object_with_array_property() {
|
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 (actual, bindings) = must_plan(program);
|
||||||
let expected = vec![
|
let expected = vec![
|
||||||
Instruction::SetPrimitive {
|
Instruction::SetPrimitive {
|
||||||
@ -712,12 +730,16 @@ fn store_object_with_array_property() {
|
|||||||
value: 1i64.into(),
|
value: 1i64.into(),
|
||||||
},
|
},
|
||||||
Instruction::SetPrimitive {
|
Instruction::SetPrimitive {
|
||||||
address: Address::ZERO.offset(1),
|
address: Address::ZERO + 1,
|
||||||
value: 2i64.into(),
|
value: 2usize.into(),
|
||||||
},
|
},
|
||||||
Instruction::SetPrimitive {
|
Instruction::SetPrimitive {
|
||||||
address: Address::ZERO.offset(2),
|
address: Address::ZERO + 2,
|
||||||
value: 3i64.into(),
|
value: 22i64.into(),
|
||||||
|
},
|
||||||
|
Instruction::SetPrimitive {
|
||||||
|
address: Address::ZERO + 3,
|
||||||
|
value: 33i64.into(),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
@ -729,9 +751,10 @@ fn store_object_with_array_property() {
|
|||||||
(
|
(
|
||||||
"b".to_owned(),
|
"b".to_owned(),
|
||||||
EpBinding::Sequence {
|
EpBinding::Sequence {
|
||||||
|
length_at: Address::ZERO.offset(1),
|
||||||
elements: vec![
|
elements: vec![
|
||||||
EpBinding::Single(Address::ZERO.offset(1)),
|
|
||||||
EpBinding::Single(Address::ZERO.offset(2)),
|
EpBinding::Single(Address::ZERO.offset(2)),
|
||||||
|
EpBinding::Single(Address::ZERO.offset(3)),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user