mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Fix argument parsing bug
Things like `luma(1, key: "val")` didn't produce an error before because `args.finish()?` wasn't called. This changes `args: Args` to `args: &mut Args` to make it impossible for that to happen.
This commit is contained in:
parent
fa81c3ece0
commit
cbfd9884a9
@ -317,7 +317,7 @@ fn create_wrapper_closure(func: &Func) -> TokenStream {
|
|||||||
.map(|tokens| quote! { #tokens, });
|
.map(|tokens| quote! { #tokens, });
|
||||||
let vm_ = func.special.vm.then(|| quote! { vm, });
|
let vm_ = func.special.vm.then(|| quote! { vm, });
|
||||||
let vt_ = func.special.vt.then(|| quote! { &mut vm.vt, });
|
let vt_ = func.special.vt.then(|| quote! { &mut vm.vt, });
|
||||||
let args_ = func.special.args.then(|| quote! { args.take(), });
|
let args_ = func.special.args.then(|| quote! { args, });
|
||||||
let span_ = func.special.span.then(|| quote! { args.span, });
|
let span_ = func.special.span.then(|| quote! { args.span, });
|
||||||
let forwarded = func.params.iter().filter(|param| !param.external).map(bind);
|
let forwarded = func.params.iter().filter(|param| !param.external).map(bind);
|
||||||
quote! {
|
quote! {
|
||||||
|
@ -354,7 +354,7 @@ impl Array {
|
|||||||
pub fn range(
|
pub fn range(
|
||||||
/// The real arguments (the other arguments are just for the docs, this
|
/// The real arguments (the other arguments are just for the docs, this
|
||||||
/// function is a bit involved, so we parse the arguments manually).
|
/// function is a bit involved, so we parse the arguments manually).
|
||||||
args: Args,
|
args: &mut Args,
|
||||||
/// The start of the range (inclusive).
|
/// The start of the range (inclusive).
|
||||||
#[external]
|
#[external]
|
||||||
#[default]
|
#[default]
|
||||||
@ -367,13 +367,11 @@ impl Array {
|
|||||||
#[default(NonZeroI64::new(1).unwrap())]
|
#[default(NonZeroI64::new(1).unwrap())]
|
||||||
step: NonZeroI64,
|
step: NonZeroI64,
|
||||||
) -> SourceResult<Array> {
|
) -> SourceResult<Array> {
|
||||||
let mut args = args;
|
|
||||||
let first = args.expect::<i64>("end")?;
|
let first = args.expect::<i64>("end")?;
|
||||||
let (start, end) = match args.eat::<i64>()? {
|
let (start, end) = match args.eat::<i64>()? {
|
||||||
Some(second) => (first, second),
|
Some(second) => (first, second),
|
||||||
None => (0, first),
|
None => (0, first),
|
||||||
};
|
};
|
||||||
args.finish()?;
|
|
||||||
|
|
||||||
let step = step.get();
|
let step = step.get();
|
||||||
|
|
||||||
@ -412,15 +410,15 @@ impl Array {
|
|||||||
/// transformed with the given function.
|
/// transformed with the given function.
|
||||||
#[func]
|
#[func]
|
||||||
pub fn map(
|
pub fn map(
|
||||||
&self,
|
self,
|
||||||
/// The virtual machine.
|
/// The virtual machine.
|
||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
/// The function to apply to each item.
|
/// The function to apply to each item.
|
||||||
mapper: Func,
|
mapper: Func,
|
||||||
) -> SourceResult<Array> {
|
) -> SourceResult<Array> {
|
||||||
self.iter()
|
self.into_iter()
|
||||||
.map(|item| {
|
.map(|item| {
|
||||||
let args = Args::new(mapper.span(), [item.clone()]);
|
let args = Args::new(mapper.span(), [item]);
|
||||||
mapper.call_vm(vm, args)
|
mapper.call_vm(vm, args)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
@ -433,20 +431,20 @@ impl Array {
|
|||||||
/// a let binding or for loop.
|
/// a let binding or for loop.
|
||||||
#[func]
|
#[func]
|
||||||
pub fn enumerate(
|
pub fn enumerate(
|
||||||
&self,
|
self,
|
||||||
/// The index returned for the first pair of the returned list.
|
/// The index returned for the first pair of the returned list.
|
||||||
#[named]
|
#[named]
|
||||||
#[default(0)]
|
#[default(0)]
|
||||||
start: i64,
|
start: i64,
|
||||||
) -> StrResult<Array> {
|
) -> StrResult<Array> {
|
||||||
self.iter()
|
self.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, value)| {
|
.map(|(i, value)| {
|
||||||
Ok(array![
|
Ok(array![
|
||||||
start
|
start
|
||||||
.checked_add_unsigned(i as u64)
|
.checked_add_unsigned(i as u64)
|
||||||
.ok_or("array index is too large")?,
|
.ok_or("array index is too large")?,
|
||||||
value.clone()
|
value
|
||||||
]
|
]
|
||||||
.into_value())
|
.into_value())
|
||||||
})
|
})
|
||||||
@ -464,33 +462,29 @@ impl Array {
|
|||||||
/// `{((1, 3, 6), (2, 4, 7), (3, 5, 8))}`.
|
/// `{((1, 3, 6), (2, 4, 7), (3, 5, 8))}`.
|
||||||
#[func]
|
#[func]
|
||||||
pub fn zip(
|
pub fn zip(
|
||||||
&self,
|
self,
|
||||||
/// The real arguments (the other arguments are just for the docs, this
|
/// The real arguments (the other arguments are just for the docs, this
|
||||||
/// function is a bit involved, so we parse the arguments manually).
|
/// function is a bit involved, so we parse the arguments manually).
|
||||||
args: Args,
|
args: &mut Args,
|
||||||
/// The arrays to zip with.
|
/// The arrays to zip with.
|
||||||
#[external]
|
#[external]
|
||||||
#[variadic]
|
#[variadic]
|
||||||
others: Vec<Array>,
|
others: Vec<Array>,
|
||||||
) -> SourceResult<Array> {
|
) -> SourceResult<Array> {
|
||||||
let mut args = args;
|
let remaining = args.remaining();
|
||||||
|
|
||||||
// Fast path for one array.
|
// Fast path for one array.
|
||||||
if args.remaining() == 0 {
|
if remaining == 0 {
|
||||||
return Ok(self
|
return Ok(self.into_iter().map(|item| array![item].into_value()).collect());
|
||||||
.iter()
|
|
||||||
.map(|item| array![item.clone()].into_value())
|
|
||||||
.collect());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fast path for just two arrays.
|
// Fast path for just two arrays.
|
||||||
if args.remaining() == 1 {
|
if remaining == 1 {
|
||||||
let other = args.expect::<Array>("others")?;
|
let other = args.expect::<Array>("others")?;
|
||||||
args.finish()?;
|
|
||||||
return Ok(self
|
return Ok(self
|
||||||
.iter()
|
.into_iter()
|
||||||
.zip(other)
|
.zip(other)
|
||||||
.map(|(first, second)| array![first.clone(), second].into_value())
|
.map(|(first, second)| array![first, second].into_value())
|
||||||
.collect());
|
.collect());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,9 +495,8 @@ impl Array {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|i| i.into_iter())
|
.map(|i| i.into_iter())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
args.finish()?;
|
|
||||||
|
|
||||||
for this in self.iter() {
|
for this in self {
|
||||||
let mut row = Self::with_capacity(1 + iterators.len());
|
let mut row = Self::with_capacity(1 + iterators.len());
|
||||||
row.push(this.clone());
|
row.push(this.clone());
|
||||||
|
|
||||||
@ -524,7 +517,7 @@ impl Array {
|
|||||||
/// Folds all items into a single value using an accumulator function.
|
/// Folds all items into a single value using an accumulator function.
|
||||||
#[func]
|
#[func]
|
||||||
pub fn fold(
|
pub fn fold(
|
||||||
&self,
|
self,
|
||||||
/// The virtual machine.
|
/// The virtual machine.
|
||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
/// The initial value to start with.
|
/// The initial value to start with.
|
||||||
@ -534,8 +527,8 @@ impl Array {
|
|||||||
folder: Func,
|
folder: Func,
|
||||||
) -> SourceResult<Value> {
|
) -> SourceResult<Value> {
|
||||||
let mut acc = init;
|
let mut acc = init;
|
||||||
for item in self.iter() {
|
for item in self {
|
||||||
let args = Args::new(folder.span(), [acc, item.clone()]);
|
let args = Args::new(folder.span(), [acc, item]);
|
||||||
acc = folder.call_vm(vm, args)?;
|
acc = folder.call_vm(vm, args)?;
|
||||||
}
|
}
|
||||||
Ok(acc)
|
Ok(acc)
|
||||||
@ -544,20 +537,19 @@ impl Array {
|
|||||||
/// Sums all items (works for all types that can be added).
|
/// Sums all items (works for all types that can be added).
|
||||||
#[func]
|
#[func]
|
||||||
pub fn sum(
|
pub fn sum(
|
||||||
&self,
|
self,
|
||||||
/// What to return if the array is empty. Must be set if the array can
|
/// What to return if the array is empty. Must be set if the array can
|
||||||
/// be empty.
|
/// be empty.
|
||||||
#[named]
|
#[named]
|
||||||
default: Option<Value>,
|
default: Option<Value>,
|
||||||
) -> StrResult<Value> {
|
) -> StrResult<Value> {
|
||||||
let mut acc = self
|
let mut iter = self.into_iter();
|
||||||
.0
|
let mut acc = iter
|
||||||
.first()
|
.next()
|
||||||
.cloned()
|
|
||||||
.or(default)
|
.or(default)
|
||||||
.ok_or("cannot calculate sum of empty array with no default")?;
|
.ok_or("cannot calculate sum of empty array with no default")?;
|
||||||
for i in self.iter().skip(1) {
|
for item in iter {
|
||||||
acc = add(acc, i.clone())?;
|
acc = add(acc, item)?;
|
||||||
}
|
}
|
||||||
Ok(acc)
|
Ok(acc)
|
||||||
}
|
}
|
||||||
@ -566,20 +558,19 @@ impl Array {
|
|||||||
/// multiplied).
|
/// multiplied).
|
||||||
#[func]
|
#[func]
|
||||||
pub fn product(
|
pub fn product(
|
||||||
&self,
|
self,
|
||||||
/// What to return if the array is empty. Must be set if the array can
|
/// What to return if the array is empty. Must be set if the array can
|
||||||
/// be empty.
|
/// be empty.
|
||||||
#[named]
|
#[named]
|
||||||
default: Option<Value>,
|
default: Option<Value>,
|
||||||
) -> StrResult<Value> {
|
) -> StrResult<Value> {
|
||||||
let mut acc = self
|
let mut iter = self.into_iter();
|
||||||
.0
|
let mut acc = iter
|
||||||
.first()
|
.next()
|
||||||
.cloned()
|
|
||||||
.or(default)
|
.or(default)
|
||||||
.ok_or("cannot calculate product of empty array with no default")?;
|
.ok_or("cannot calculate product of empty array with no default")?;
|
||||||
for i in self.iter().skip(1) {
|
for item in iter {
|
||||||
acc = mul(acc, i.clone())?;
|
acc = mul(acc, item)?;
|
||||||
}
|
}
|
||||||
Ok(acc)
|
Ok(acc)
|
||||||
}
|
}
|
||||||
@ -587,14 +578,14 @@ impl Array {
|
|||||||
/// Whether the given function returns `{true}` for any item in the array.
|
/// Whether the given function returns `{true}` for any item in the array.
|
||||||
#[func]
|
#[func]
|
||||||
pub fn any(
|
pub fn any(
|
||||||
&self,
|
self,
|
||||||
/// The virtual machine.
|
/// The virtual machine.
|
||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
/// The function to apply to each item. Must return a boolean.
|
/// The function to apply to each item. Must return a boolean.
|
||||||
test: Func,
|
test: Func,
|
||||||
) -> SourceResult<bool> {
|
) -> SourceResult<bool> {
|
||||||
for item in self.iter() {
|
for item in self {
|
||||||
let args = Args::new(test.span(), [item.clone()]);
|
let args = Args::new(test.span(), [item]);
|
||||||
if test.call_vm(vm, args)?.cast::<bool>().at(test.span())? {
|
if test.call_vm(vm, args)?.cast::<bool>().at(test.span())? {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
@ -606,14 +597,14 @@ impl Array {
|
|||||||
/// Whether the given function returns `{true}` for all items in the array.
|
/// Whether the given function returns `{true}` for all items in the array.
|
||||||
#[func]
|
#[func]
|
||||||
pub fn all(
|
pub fn all(
|
||||||
&self,
|
self,
|
||||||
/// The virtual machine.
|
/// The virtual machine.
|
||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
/// The function to apply to each item. Must return a boolean.
|
/// The function to apply to each item. Must return a boolean.
|
||||||
test: Func,
|
test: Func,
|
||||||
) -> SourceResult<bool> {
|
) -> SourceResult<bool> {
|
||||||
for item in self.iter() {
|
for item in self {
|
||||||
let args = Args::new(test.span(), [item.clone()]);
|
let args = Args::new(test.span(), [item]);
|
||||||
if !test.call_vm(vm, args)?.cast::<bool>().at(test.span())? {
|
if !test.call_vm(vm, args)?.cast::<bool>().at(test.span())? {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
@ -624,13 +615,13 @@ impl Array {
|
|||||||
|
|
||||||
/// Combine all nested arrays into a single flat one.
|
/// Combine all nested arrays into a single flat one.
|
||||||
#[func]
|
#[func]
|
||||||
pub fn flatten(&self) -> Array {
|
pub fn flatten(self) -> Array {
|
||||||
let mut flat = EcoVec::with_capacity(self.0.len());
|
let mut flat = EcoVec::with_capacity(self.0.len());
|
||||||
for item in self.iter() {
|
for item in self {
|
||||||
if let Value::Array(nested) = item {
|
if let Value::Array(nested) = item {
|
||||||
flat.extend(nested.flatten().into_iter());
|
flat.extend(nested.flatten());
|
||||||
} else {
|
} else {
|
||||||
flat.push(item.clone());
|
flat.push(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
flat.into()
|
flat.into()
|
||||||
@ -638,8 +629,8 @@ impl Array {
|
|||||||
|
|
||||||
/// Return a new array with the same items, but in reverse order.
|
/// Return a new array with the same items, but in reverse order.
|
||||||
#[func(title = "Reverse")]
|
#[func(title = "Reverse")]
|
||||||
pub fn rev(&self) -> Array {
|
pub fn rev(self) -> Array {
|
||||||
self.0.iter().cloned().rev().collect()
|
self.into_iter().rev().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Split the array at occurrences of the specified value.
|
/// Split the array at occurrences of the specified value.
|
||||||
@ -658,7 +649,7 @@ impl Array {
|
|||||||
/// Combine all items in the array into one.
|
/// Combine all items in the array into one.
|
||||||
#[func]
|
#[func]
|
||||||
pub fn join(
|
pub fn join(
|
||||||
&self,
|
self,
|
||||||
/// A value to insert between each item of the array.
|
/// A value to insert between each item of the array.
|
||||||
#[default]
|
#[default]
|
||||||
separator: Option<Value>,
|
separator: Option<Value>,
|
||||||
@ -671,7 +662,7 @@ impl Array {
|
|||||||
|
|
||||||
let mut last = last;
|
let mut last = last;
|
||||||
let mut result = Value::None;
|
let mut result = Value::None;
|
||||||
for (i, value) in self.iter().cloned().enumerate() {
|
for (i, value) in self.into_iter().enumerate() {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
if i + 1 == len && last.is_some() {
|
if i + 1 == len && last.is_some() {
|
||||||
result = ops::join(result, last.take().unwrap())?;
|
result = ops::join(result, last.take().unwrap())?;
|
||||||
@ -690,7 +681,7 @@ impl Array {
|
|||||||
/// adjacent elements.
|
/// adjacent elements.
|
||||||
#[func]
|
#[func]
|
||||||
pub fn intersperse(
|
pub fn intersperse(
|
||||||
&self,
|
self,
|
||||||
/// The value that will be placed between each adjacent element.
|
/// The value that will be placed between each adjacent element.
|
||||||
separator: Value,
|
separator: Value,
|
||||||
) -> Array {
|
) -> Array {
|
||||||
@ -701,7 +692,7 @@ impl Array {
|
|||||||
n => (2 * n) - 1,
|
n => (2 * n) - 1,
|
||||||
};
|
};
|
||||||
let mut vec = EcoVec::with_capacity(size);
|
let mut vec = EcoVec::with_capacity(size);
|
||||||
let mut iter = self.iter().cloned();
|
let mut iter = self.into_iter();
|
||||||
|
|
||||||
if let Some(first) = iter.next() {
|
if let Some(first) = iter.next() {
|
||||||
vec.push(first);
|
vec.push(first);
|
||||||
@ -722,7 +713,7 @@ impl Array {
|
|||||||
/// function (if given) yields an error.
|
/// function (if given) yields an error.
|
||||||
#[func]
|
#[func]
|
||||||
pub fn sorted(
|
pub fn sorted(
|
||||||
&self,
|
self,
|
||||||
/// The virtual machine.
|
/// The virtual machine.
|
||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
/// The callsite span.
|
/// The callsite span.
|
||||||
@ -733,7 +724,7 @@ impl Array {
|
|||||||
key: Option<Func>,
|
key: Option<Func>,
|
||||||
) -> SourceResult<Array> {
|
) -> SourceResult<Array> {
|
||||||
let mut result = Ok(());
|
let mut result = Ok(());
|
||||||
let mut vec = self.0.clone();
|
let mut vec = self.0;
|
||||||
let mut key_of = |x: Value| match &key {
|
let mut key_of = |x: Value| match &key {
|
||||||
// NOTE: We are relying on `comemo`'s memoization of function
|
// NOTE: We are relying on `comemo`'s memoization of function
|
||||||
// evaluation to not excessively reevaluate the `key`.
|
// evaluation to not excessively reevaluate the `key`.
|
||||||
@ -772,7 +763,7 @@ impl Array {
|
|||||||
/// ```
|
/// ```
|
||||||
#[func(title = "Deduplicate")]
|
#[func(title = "Deduplicate")]
|
||||||
pub fn dedup(
|
pub fn dedup(
|
||||||
&self,
|
self,
|
||||||
/// The virtual machine.
|
/// The virtual machine.
|
||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
/// If given, applies this function to the elements in the array to
|
/// If given, applies this function to the elements in the array to
|
||||||
@ -791,10 +782,10 @@ impl Array {
|
|||||||
// This algorithm is O(N^2) because we cannot rely on `HashSet` since:
|
// This algorithm is O(N^2) because we cannot rely on `HashSet` since:
|
||||||
// 1. We would like to preserve the order of the elements.
|
// 1. We would like to preserve the order of the elements.
|
||||||
// 2. We cannot hash arbitrary `Value`.
|
// 2. We cannot hash arbitrary `Value`.
|
||||||
'outer: for value in self.iter() {
|
'outer: for value in self {
|
||||||
let key = key_of(value.clone())?;
|
let key = key_of(value.clone())?;
|
||||||
if out.is_empty() {
|
if out.is_empty() {
|
||||||
out.push(value.clone());
|
out.push(value);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -804,7 +795,7 @@ impl Array {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out.push(value.clone());
|
out.push(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self(out))
|
Ok(Self(out))
|
||||||
|
@ -331,14 +331,17 @@ impl Func {
|
|||||||
self,
|
self,
|
||||||
/// The real arguments (the other argument is just for the docs).
|
/// The real arguments (the other argument is just for the docs).
|
||||||
/// The docs argument cannot be called `args`.
|
/// The docs argument cannot be called `args`.
|
||||||
args: Args,
|
args: &mut Args,
|
||||||
/// The arguments to apply to the function.
|
/// The arguments to apply to the function.
|
||||||
#[external]
|
#[external]
|
||||||
#[variadic]
|
#[variadic]
|
||||||
arguments: Vec<Args>,
|
arguments: Vec<Args>,
|
||||||
) -> Func {
|
) -> Func {
|
||||||
let span = self.span;
|
let span = self.span;
|
||||||
Self { repr: Repr::With(Arc::new((self, args))), span }
|
Self {
|
||||||
|
repr: Repr::With(Arc::new((self, args.take()))),
|
||||||
|
span,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a selector that filters for elements belonging to this function
|
/// Returns a selector that filters for elements belonging to this function
|
||||||
@ -348,13 +351,12 @@ impl Func {
|
|||||||
self,
|
self,
|
||||||
/// The real arguments (the other argument is just for the docs).
|
/// The real arguments (the other argument is just for the docs).
|
||||||
/// The docs argument cannot be called `args`.
|
/// The docs argument cannot be called `args`.
|
||||||
args: Args,
|
args: &mut Args,
|
||||||
/// The fields to filter for.
|
/// The fields to filter for.
|
||||||
#[variadic]
|
#[variadic]
|
||||||
#[external]
|
#[external]
|
||||||
fields: Vec<Args>,
|
fields: Vec<Args>,
|
||||||
) -> StrResult<Selector> {
|
) -> StrResult<Selector> {
|
||||||
let mut args = args;
|
|
||||||
let fields = args.to_named();
|
let fields = args.to_named();
|
||||||
args.items.retain(|arg| arg.name.is_none());
|
args.items.retain(|arg| arg.name.is_none());
|
||||||
Ok(self
|
Ok(self
|
||||||
|
@ -254,7 +254,7 @@ impl Color {
|
|||||||
pub fn luma(
|
pub fn luma(
|
||||||
/// The real arguments (the other arguments are just for the docs, this
|
/// The real arguments (the other arguments are just for the docs, this
|
||||||
/// function is a bit involved, so we parse the arguments manually).
|
/// function is a bit involved, so we parse the arguments manually).
|
||||||
args: Args,
|
args: &mut Args,
|
||||||
/// The lightness component.
|
/// The lightness component.
|
||||||
#[external]
|
#[external]
|
||||||
lightness: Component,
|
lightness: Component,
|
||||||
@ -264,7 +264,6 @@ impl Color {
|
|||||||
#[external]
|
#[external]
|
||||||
color: Color,
|
color: Color,
|
||||||
) -> SourceResult<Color> {
|
) -> SourceResult<Color> {
|
||||||
let mut args = args;
|
|
||||||
Ok(if let Some(color) = args.find::<Color>()? {
|
Ok(if let Some(color) = args.find::<Color>()? {
|
||||||
color.to_luma()
|
color.to_luma()
|
||||||
} else {
|
} else {
|
||||||
@ -300,7 +299,7 @@ impl Color {
|
|||||||
pub fn oklab(
|
pub fn oklab(
|
||||||
/// The real arguments (the other arguments are just for the docs, this
|
/// The real arguments (the other arguments are just for the docs, this
|
||||||
/// function is a bit involved, so we parse the arguments manually).
|
/// function is a bit involved, so we parse the arguments manually).
|
||||||
args: Args,
|
args: &mut Args,
|
||||||
/// The cyan component.
|
/// The cyan component.
|
||||||
#[external]
|
#[external]
|
||||||
lightness: RatioComponent,
|
lightness: RatioComponent,
|
||||||
@ -319,7 +318,6 @@ impl Color {
|
|||||||
#[external]
|
#[external]
|
||||||
color: Color,
|
color: Color,
|
||||||
) -> SourceResult<Color> {
|
) -> SourceResult<Color> {
|
||||||
let mut args = args;
|
|
||||||
Ok(if let Some(color) = args.find::<Color>()? {
|
Ok(if let Some(color) = args.find::<Color>()? {
|
||||||
color.to_oklab()
|
color.to_oklab()
|
||||||
} else {
|
} else {
|
||||||
@ -363,7 +361,7 @@ impl Color {
|
|||||||
pub fn linear_rgb(
|
pub fn linear_rgb(
|
||||||
/// The real arguments (the other arguments are just for the docs, this
|
/// The real arguments (the other arguments are just for the docs, this
|
||||||
/// function is a bit involved, so we parse the arguments manually).
|
/// function is a bit involved, so we parse the arguments manually).
|
||||||
args: Args,
|
args: &mut Args,
|
||||||
/// The red component.
|
/// The red component.
|
||||||
#[external]
|
#[external]
|
||||||
red: Component,
|
red: Component,
|
||||||
@ -382,7 +380,6 @@ impl Color {
|
|||||||
#[external]
|
#[external]
|
||||||
color: Color,
|
color: Color,
|
||||||
) -> SourceResult<Color> {
|
) -> SourceResult<Color> {
|
||||||
let mut args = args;
|
|
||||||
Ok(if let Some(color) = args.find::<Color>()? {
|
Ok(if let Some(color) = args.find::<Color>()? {
|
||||||
color.to_linear_rgb()
|
color.to_linear_rgb()
|
||||||
} else {
|
} else {
|
||||||
@ -421,7 +418,7 @@ impl Color {
|
|||||||
pub fn rgb(
|
pub fn rgb(
|
||||||
/// The real arguments (the other arguments are just for the docs, this
|
/// The real arguments (the other arguments are just for the docs, this
|
||||||
/// function is a bit involved, so we parse the arguments manually).
|
/// function is a bit involved, so we parse the arguments manually).
|
||||||
args: Args,
|
args: &mut Args,
|
||||||
/// The red component.
|
/// The red component.
|
||||||
#[external]
|
#[external]
|
||||||
red: Component,
|
red: Component,
|
||||||
@ -454,7 +451,6 @@ impl Color {
|
|||||||
#[external]
|
#[external]
|
||||||
color: Color,
|
color: Color,
|
||||||
) -> SourceResult<Color> {
|
) -> SourceResult<Color> {
|
||||||
let mut args = args;
|
|
||||||
Ok(if let Some(string) = args.find::<Spanned<Str>>()? {
|
Ok(if let Some(string) = args.find::<Spanned<Str>>()? {
|
||||||
Self::from_str(&string.v).at(string.span)?
|
Self::from_str(&string.v).at(string.span)?
|
||||||
} else if let Some(color) = args.find::<Color>()? {
|
} else if let Some(color) = args.find::<Color>()? {
|
||||||
@ -497,7 +493,7 @@ impl Color {
|
|||||||
pub fn cmyk(
|
pub fn cmyk(
|
||||||
/// The real arguments (the other arguments are just for the docs, this
|
/// The real arguments (the other arguments are just for the docs, this
|
||||||
/// function is a bit involved, so we parse the arguments manually).
|
/// function is a bit involved, so we parse the arguments manually).
|
||||||
args: Args,
|
args: &mut Args,
|
||||||
/// The cyan component.
|
/// The cyan component.
|
||||||
#[external]
|
#[external]
|
||||||
cyan: RatioComponent,
|
cyan: RatioComponent,
|
||||||
@ -516,7 +512,6 @@ impl Color {
|
|||||||
#[external]
|
#[external]
|
||||||
color: Color,
|
color: Color,
|
||||||
) -> SourceResult<Color> {
|
) -> SourceResult<Color> {
|
||||||
let mut args = args;
|
|
||||||
Ok(if let Some(color) = args.find::<Color>()? {
|
Ok(if let Some(color) = args.find::<Color>()? {
|
||||||
color.to_cmyk()
|
color.to_cmyk()
|
||||||
} else {
|
} else {
|
||||||
@ -557,7 +552,7 @@ impl Color {
|
|||||||
pub fn hsl(
|
pub fn hsl(
|
||||||
/// The real arguments (the other arguments are just for the docs, this
|
/// The real arguments (the other arguments are just for the docs, this
|
||||||
/// function is a bit involved, so we parse the arguments manually).
|
/// function is a bit involved, so we parse the arguments manually).
|
||||||
args: Args,
|
args: &mut Args,
|
||||||
/// The hue angle.
|
/// The hue angle.
|
||||||
#[external]
|
#[external]
|
||||||
hue: Angle,
|
hue: Angle,
|
||||||
@ -576,7 +571,6 @@ impl Color {
|
|||||||
#[external]
|
#[external]
|
||||||
color: Color,
|
color: Color,
|
||||||
) -> SourceResult<Color> {
|
) -> SourceResult<Color> {
|
||||||
let mut args = args;
|
|
||||||
Ok(if let Some(color) = args.find::<Color>()? {
|
Ok(if let Some(color) = args.find::<Color>()? {
|
||||||
color.to_hsl()
|
color.to_hsl()
|
||||||
} else {
|
} else {
|
||||||
@ -617,7 +611,7 @@ impl Color {
|
|||||||
pub fn hsv(
|
pub fn hsv(
|
||||||
/// The real arguments (the other arguments are just for the docs, this
|
/// The real arguments (the other arguments are just for the docs, this
|
||||||
/// function is a bit involved, so we parse the arguments manually).
|
/// function is a bit involved, so we parse the arguments manually).
|
||||||
args: Args,
|
args: &mut Args,
|
||||||
/// The hue angle.
|
/// The hue angle.
|
||||||
#[external]
|
#[external]
|
||||||
hue: Angle,
|
hue: Angle,
|
||||||
@ -636,7 +630,6 @@ impl Color {
|
|||||||
#[external]
|
#[external]
|
||||||
color: Color,
|
color: Color,
|
||||||
) -> SourceResult<Color> {
|
) -> SourceResult<Color> {
|
||||||
let mut args = args;
|
|
||||||
Ok(if let Some(color) = args.find::<Color>()? {
|
Ok(if let Some(color) = args.find::<Color>()? {
|
||||||
color.to_hsv()
|
color.to_hsv()
|
||||||
} else {
|
} else {
|
||||||
|
@ -182,7 +182,7 @@ impl Gradient {
|
|||||||
#[func(title = "Linear Gradient")]
|
#[func(title = "Linear Gradient")]
|
||||||
pub fn linear(
|
pub fn linear(
|
||||||
/// The args of this function.
|
/// The args of this function.
|
||||||
args: Args,
|
args: &mut Args,
|
||||||
/// The call site of this function.
|
/// The call site of this function.
|
||||||
span: Span,
|
span: Span,
|
||||||
/// The color [stops](#stops) of the gradient.
|
/// The color [stops](#stops) of the gradient.
|
||||||
@ -212,12 +212,6 @@ impl Gradient {
|
|||||||
#[external]
|
#[external]
|
||||||
angle: Angle,
|
angle: Angle,
|
||||||
) -> SourceResult<Gradient> {
|
) -> SourceResult<Gradient> {
|
||||||
let mut args = args;
|
|
||||||
if stops.len() < 2 {
|
|
||||||
bail!(error!(span, "a gradient must have at least two stops")
|
|
||||||
.with_hint("try filling the shape with a single color instead"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let angle = if let Some(angle) = args.named::<Angle>("angle")? {
|
let angle = if let Some(angle) = args.named::<Angle>("angle")? {
|
||||||
angle
|
angle
|
||||||
} else if let Some(dir) = args.named::<Dir>("dir")? {
|
} else if let Some(dir) = args.named::<Dir>("dir")? {
|
||||||
@ -231,6 +225,11 @@ impl Gradient {
|
|||||||
Angle::rad(0.0)
|
Angle::rad(0.0)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if stops.len() < 2 {
|
||||||
|
bail!(error!(span, "a gradient must have at least two stops")
|
||||||
|
.with_hint("try filling the shape with a single color instead"));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Self::Linear(Arc::new(LinearGradient {
|
Ok(Self::Linear(Arc::new(LinearGradient {
|
||||||
stops: process_stops(&stops)?,
|
stops: process_stops(&stops)?,
|
||||||
angle,
|
angle,
|
||||||
|
@ -248,6 +248,10 @@
|
|||||||
#test((2, 1, 3, -10, -5, 8, 6, -7, 2).sorted(key: x => x), (-10, -7, -5, 1, 2, 2, 3, 6, 8))
|
#test((2, 1, 3, -10, -5, 8, 6, -7, 2).sorted(key: x => x), (-10, -7, -5, 1, 2, 2, 3, 6, 8))
|
||||||
#test((2, 1, 3, -10, -5, 8, 6, -7, 2).sorted(key: x => x * x), (1, 2, 2, 3, -5, 6, -7, 8, -10))
|
#test((2, 1, 3, -10, -5, 8, 6, -7, 2).sorted(key: x => x * x), (1, 2, 2, 3, -5, 6, -7, 8, -10))
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 12-18 unexpected argument
|
||||||
|
#().sorted(x => x)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test the `zip` method.
|
// Test the `zip` method.
|
||||||
#test(().zip(()), ())
|
#test(().zip(()), ())
|
||||||
@ -262,7 +266,7 @@
|
|||||||
#test((1,).zip((2,), (3,)), ((1, 2, 3),))
|
#test((1,).zip((2,), (3,)), ((1, 2, 3),))
|
||||||
#test((1, 2, 3).zip(), ((1,), (2,), (3,)))
|
#test((1, 2, 3).zip(), ((1,), (2,), (3,)))
|
||||||
#test(array.zip(()), ())
|
#test(array.zip(()), ())
|
||||||
#test(array.zip(("a", "b")), (("a",), ("b",)))
|
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test the `enumerate` method.
|
// Test the `enumerate` method.
|
||||||
@ -289,6 +293,14 @@
|
|||||||
#test(("Hello", "World", "Hi", "There").dedup(key: x => x.len()), ("Hello", "Hi"))
|
#test(("Hello", "World", "Hi", "There").dedup(key: x => x.len()), ("Hello", "Hi"))
|
||||||
#test(("Hello", "World", "Hi", "There").dedup(key: x => x.at(0)), ("Hello", "World", "There"))
|
#test(("Hello", "World", "Hi", "There").dedup(key: x => x.at(0)), ("Hello", "World", "There"))
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 9-26 unexpected argument: val
|
||||||
|
#().zip(val: "applicable")
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 13-30 unexpected argument: val
|
||||||
|
#().zip((), val: "applicable")
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 32-37 cannot divide by zero
|
// Error: 32-37 cannot divide by zero
|
||||||
#(1, 2, 0, 3).sorted(key: x => 5 / x)
|
#(1, 2, 0, 3).sorted(key: x => 5 / x)
|
||||||
|
@ -116,6 +116,10 @@
|
|||||||
// Error: 21-26 expected integer or ratio, found boolean
|
// Error: 21-26 expected integer or ratio, found boolean
|
||||||
#rgb(10%, 20%, 30%, false)
|
#rgb(10%, 20%, 30%, false)
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 10-20 unexpected argument: key
|
||||||
|
#luma(1, key: "val")
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 12-24 expected float or ratio, found string
|
// Error: 12-24 expected float or ratio, found string
|
||||||
// Error: 26-39 expected float or ratio, found string
|
// Error: 26-39 expected float or ratio, found string
|
||||||
|
Loading…
x
Reference in New Issue
Block a user