swizzle method
.xyz and .rgb, folded on a constant.
Implementation
Expression swizzle(Expression target, ExpressionToken field) {
if (!target.type.isNumeric) {
fail('a ${target.type} has no components.', where: field);
}
const sets = <String>['xyzw', 'rgba'];
final indices = <int>[];
String? chosen;
for (final letter in field.text.split('')) {
final set = sets.firstWhere((s) => s.contains(letter), orElse: () => '');
if (set.isEmpty) {
fail(
'"$letter" is not a component: xyzw and rgba are, and one '
'swizzle does not mix the two.',
where: field,
);
}
// GLSL refuses `.xg` too, and for the reason it reads badly rather than
// for one about the hardware: two names for the same component in one
// expression is somebody having lost track of which vector they are in.
chosen ??= set;
if (set != chosen) {
fail('"${field.text}" mixes xyzw with rgba.', where: field);
}
final index = set.indexOf(letter);
if (index >= target.type.components) {
fail('a ${target.type} has no "$letter".', where: field);
}
indices.add(index);
}
if (indices.isEmpty || indices.length > 4) {
fail('"${field.text}" is not one to four components.', where: field);
}
final type = ExpressionType.numeric[indices.length]!.withDimension(
target.type.dimension,
);
if (target is ExpressionConstant) {
return ExpressionConstant(<double>[
for (final index in indices) target.value[index],
], type);
}
return ExpressionSwizzle(target, indices, type);
}