mirror of
https://github.com/typst/typst
synced 2025-05-18 02:55:28 +08:00
Bring back type/str compatibility for 0.13, with warnings and hints (#5877)
This commit is contained in:
parent
2f1a5ab914
commit
e294fe85a5
@ -55,7 +55,7 @@ fn eval_code<'a>(
|
|||||||
_ => expr.eval(vm)?,
|
_ => expr.eval(vm)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
output = ops::join(output, value).at(span)?;
|
output = ops::join(output, value, &mut (&mut vm.engine, span)).at(span)?;
|
||||||
|
|
||||||
if let Some(event) = &vm.flow {
|
if let Some(event) = &vm.flow {
|
||||||
warn_for_discarded_content(&mut vm.engine, event, &output);
|
warn_for_discarded_content(&mut vm.engine, event, &output);
|
||||||
|
@ -83,7 +83,8 @@ impl Eval for ast::WhileLoop<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let value = body.eval(vm)?;
|
let value = body.eval(vm)?;
|
||||||
output = ops::join(output, value).at(body.span())?;
|
let span = body.span();
|
||||||
|
output = ops::join(output, value, &mut (&mut vm.engine, span)).at(span)?;
|
||||||
|
|
||||||
match vm.flow {
|
match vm.flow {
|
||||||
Some(FlowEvent::Break(_)) => {
|
Some(FlowEvent::Break(_)) => {
|
||||||
@ -129,7 +130,9 @@ impl Eval for ast::ForLoop<'_> {
|
|||||||
|
|
||||||
let body = self.body();
|
let body = self.body();
|
||||||
let value = body.eval(vm)?;
|
let value = body.eval(vm)?;
|
||||||
output = ops::join(output, value).at(body.span())?;
|
let span = body.span();
|
||||||
|
output =
|
||||||
|
ops::join(output, value, &mut (&mut vm.engine, span)).at(span)?;
|
||||||
|
|
||||||
match vm.flow {
|
match vm.flow {
|
||||||
Some(FlowEvent::Break(_)) => {
|
Some(FlowEvent::Break(_)) => {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use typst_library::diag::{At, HintedStrResult, SourceResult};
|
use typst_library::diag::{At, DeprecationSink, HintedStrResult, SourceResult};
|
||||||
use typst_library::foundations::{ops, IntoValue, Value};
|
use typst_library::foundations::{ops, IntoValue, Value};
|
||||||
use typst_syntax::ast::{self, AstNode};
|
use typst_syntax::ast::{self, AstNode};
|
||||||
|
|
||||||
@ -23,22 +23,22 @@ impl Eval for ast::Binary<'_> {
|
|||||||
|
|
||||||
fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
match self.op() {
|
match self.op() {
|
||||||
ast::BinOp::Add => apply_binary(self, vm, ops::add),
|
ast::BinOp::Add => apply_binary_with_sink(self, vm, ops::add),
|
||||||
ast::BinOp::Sub => apply_binary(self, vm, ops::sub),
|
ast::BinOp::Sub => apply_binary(self, vm, ops::sub),
|
||||||
ast::BinOp::Mul => apply_binary(self, vm, ops::mul),
|
ast::BinOp::Mul => apply_binary(self, vm, ops::mul),
|
||||||
ast::BinOp::Div => apply_binary(self, vm, ops::div),
|
ast::BinOp::Div => apply_binary(self, vm, ops::div),
|
||||||
ast::BinOp::And => apply_binary(self, vm, ops::and),
|
ast::BinOp::And => apply_binary(self, vm, ops::and),
|
||||||
ast::BinOp::Or => apply_binary(self, vm, ops::or),
|
ast::BinOp::Or => apply_binary(self, vm, ops::or),
|
||||||
ast::BinOp::Eq => apply_binary(self, vm, ops::eq),
|
ast::BinOp::Eq => apply_binary_with_sink(self, vm, ops::eq),
|
||||||
ast::BinOp::Neq => apply_binary(self, vm, ops::neq),
|
ast::BinOp::Neq => apply_binary_with_sink(self, vm, ops::neq),
|
||||||
ast::BinOp::Lt => apply_binary(self, vm, ops::lt),
|
ast::BinOp::Lt => apply_binary(self, vm, ops::lt),
|
||||||
ast::BinOp::Leq => apply_binary(self, vm, ops::leq),
|
ast::BinOp::Leq => apply_binary(self, vm, ops::leq),
|
||||||
ast::BinOp::Gt => apply_binary(self, vm, ops::gt),
|
ast::BinOp::Gt => apply_binary(self, vm, ops::gt),
|
||||||
ast::BinOp::Geq => apply_binary(self, vm, ops::geq),
|
ast::BinOp::Geq => apply_binary(self, vm, ops::geq),
|
||||||
ast::BinOp::In => apply_binary(self, vm, ops::in_),
|
ast::BinOp::In => apply_binary_with_sink(self, vm, ops::in_),
|
||||||
ast::BinOp::NotIn => apply_binary(self, vm, ops::not_in),
|
ast::BinOp::NotIn => apply_binary_with_sink(self, vm, ops::not_in),
|
||||||
ast::BinOp::Assign => apply_assignment(self, vm, |_, b| Ok(b)),
|
ast::BinOp::Assign => apply_assignment(self, vm, |_, b| Ok(b)),
|
||||||
ast::BinOp::AddAssign => apply_assignment(self, vm, ops::add),
|
ast::BinOp::AddAssign => apply_assignment_with_sink(self, vm, ops::add),
|
||||||
ast::BinOp::SubAssign => apply_assignment(self, vm, ops::sub),
|
ast::BinOp::SubAssign => apply_assignment(self, vm, ops::sub),
|
||||||
ast::BinOp::MulAssign => apply_assignment(self, vm, ops::mul),
|
ast::BinOp::MulAssign => apply_assignment(self, vm, ops::mul),
|
||||||
ast::BinOp::DivAssign => apply_assignment(self, vm, ops::div),
|
ast::BinOp::DivAssign => apply_assignment(self, vm, ops::div),
|
||||||
@ -65,6 +65,18 @@ fn apply_binary(
|
|||||||
op(lhs, rhs).at(binary.span())
|
op(lhs, rhs).at(binary.span())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Apply a basic binary operation, with the possiblity of deprecations.
|
||||||
|
fn apply_binary_with_sink(
|
||||||
|
binary: ast::Binary,
|
||||||
|
vm: &mut Vm,
|
||||||
|
op: impl Fn(Value, Value, &mut dyn DeprecationSink) -> HintedStrResult<Value>,
|
||||||
|
) -> SourceResult<Value> {
|
||||||
|
let span = binary.span();
|
||||||
|
let lhs = binary.lhs().eval(vm)?;
|
||||||
|
let rhs = binary.rhs().eval(vm)?;
|
||||||
|
op(lhs, rhs, &mut (&mut vm.engine, span)).at(span)
|
||||||
|
}
|
||||||
|
|
||||||
/// Apply an assignment operation.
|
/// Apply an assignment operation.
|
||||||
fn apply_assignment(
|
fn apply_assignment(
|
||||||
binary: ast::Binary,
|
binary: ast::Binary,
|
||||||
@ -89,3 +101,23 @@ fn apply_assignment(
|
|||||||
*location = op(lhs, rhs).at(binary.span())?;
|
*location = op(lhs, rhs).at(binary.span())?;
|
||||||
Ok(Value::None)
|
Ok(Value::None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Apply an assignment operation, with the possiblity of deprecations.
|
||||||
|
fn apply_assignment_with_sink(
|
||||||
|
binary: ast::Binary,
|
||||||
|
vm: &mut Vm,
|
||||||
|
op: fn(Value, Value, &mut dyn DeprecationSink) -> HintedStrResult<Value>,
|
||||||
|
) -> SourceResult<Value> {
|
||||||
|
let rhs = binary.rhs().eval(vm)?;
|
||||||
|
let location = binary.lhs().access(vm)?;
|
||||||
|
let lhs = std::mem::take(&mut *location);
|
||||||
|
let mut sink = vec![];
|
||||||
|
let span = binary.span();
|
||||||
|
*location = op(lhs, rhs, &mut (&mut sink, span)).at(span)?;
|
||||||
|
if !sink.is_empty() {
|
||||||
|
for warning in sink {
|
||||||
|
vm.engine.sink.warn(warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Value::None)
|
||||||
|
}
|
||||||
|
@ -232,18 +232,42 @@ impl From<SyntaxError> for SourceDiagnostic {
|
|||||||
/// Destination for a deprecation message when accessing a deprecated value.
|
/// Destination for a deprecation message when accessing a deprecated value.
|
||||||
pub trait DeprecationSink {
|
pub trait DeprecationSink {
|
||||||
/// Emits the given deprecation message into this sink.
|
/// Emits the given deprecation message into this sink.
|
||||||
fn emit(self, message: &str);
|
fn emit(&mut self, message: &str);
|
||||||
|
|
||||||
|
/// Emits the given deprecation message into this sink, with the given
|
||||||
|
/// hints.
|
||||||
|
fn emit_with_hints(&mut self, message: &str, hints: &[&str]);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeprecationSink for () {
|
impl DeprecationSink for () {
|
||||||
fn emit(self, _: &str) {}
|
fn emit(&mut self, _: &str) {}
|
||||||
|
fn emit_with_hints(&mut self, _: &str, _: &[&str]) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeprecationSink for (&mut Vec<SourceDiagnostic>, Span) {
|
||||||
|
fn emit(&mut self, message: &str) {
|
||||||
|
self.0.push(SourceDiagnostic::warning(self.1, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_with_hints(&mut self, message: &str, hints: &[&str]) {
|
||||||
|
self.0.push(
|
||||||
|
SourceDiagnostic::warning(self.1, message)
|
||||||
|
.with_hints(hints.iter().copied().map(Into::into)),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeprecationSink for (&mut Engine<'_>, Span) {
|
impl DeprecationSink for (&mut Engine<'_>, Span) {
|
||||||
/// Emits the deprecation message as a warning.
|
fn emit(&mut self, message: &str) {
|
||||||
fn emit(self, message: &str) {
|
|
||||||
self.0.sink.warn(SourceDiagnostic::warning(self.1, message));
|
self.0.sink.warn(SourceDiagnostic::warning(self.1, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn emit_with_hints(&mut self, message: &str, hints: &[&str]) {
|
||||||
|
self.0.sink.warn(
|
||||||
|
SourceDiagnostic::warning(self.1, message)
|
||||||
|
.with_hints(hints.iter().copied().map(Into::into)),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A part of a diagnostic's [trace](SourceDiagnostic::trace).
|
/// A part of a diagnostic's [trace](SourceDiagnostic::trace).
|
||||||
|
@ -9,7 +9,9 @@ use serde::{Deserialize, Serialize};
|
|||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use typst_syntax::{Span, Spanned};
|
use typst_syntax::{Span, Spanned};
|
||||||
|
|
||||||
use crate::diag::{bail, At, HintedStrResult, SourceDiagnostic, SourceResult, StrResult};
|
use crate::diag::{
|
||||||
|
bail, At, DeprecationSink, HintedStrResult, SourceDiagnostic, SourceResult, StrResult,
|
||||||
|
};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
cast, func, ops, repr, scope, ty, Args, Bytes, CastInfo, Context, Dict, FromValue,
|
cast, func, ops, repr, scope, ty, Args, Bytes, CastInfo, Context, Dict, FromValue,
|
||||||
@ -143,6 +145,11 @@ impl Array {
|
|||||||
|
|
||||||
Ok(self.iter().cloned().cycle().take(count).collect())
|
Ok(self.iter().cloned().cycle().take(count).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The internal implementation of [`Array::contains`].
|
||||||
|
pub fn contains_impl(&self, value: &Value, sink: &mut dyn DeprecationSink) -> bool {
|
||||||
|
self.0.iter().any(|v| ops::equal(v, value, sink))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[scope]
|
#[scope]
|
||||||
@ -290,10 +297,12 @@ impl Array {
|
|||||||
#[func]
|
#[func]
|
||||||
pub fn contains(
|
pub fn contains(
|
||||||
&self,
|
&self,
|
||||||
|
engine: &mut Engine,
|
||||||
|
span: Span,
|
||||||
/// The value to search for.
|
/// The value to search for.
|
||||||
value: Value,
|
value: Value,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.0.contains(&value)
|
self.contains_impl(&value, &mut (engine, span))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Searches for an item for which the given function returns `{true}` and
|
/// Searches for an item for which the given function returns `{true}` and
|
||||||
@ -576,6 +585,8 @@ impl Array {
|
|||||||
#[func]
|
#[func]
|
||||||
pub fn sum(
|
pub fn sum(
|
||||||
self,
|
self,
|
||||||
|
engine: &mut Engine,
|
||||||
|
span: Span,
|
||||||
/// 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]
|
||||||
@ -587,7 +598,7 @@ impl Array {
|
|||||||
.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 item in iter {
|
for item in iter {
|
||||||
acc = ops::add(acc, item)?;
|
acc = ops::add(acc, item, &mut (&mut *engine, span))?;
|
||||||
}
|
}
|
||||||
Ok(acc)
|
Ok(acc)
|
||||||
}
|
}
|
||||||
@ -686,6 +697,8 @@ impl Array {
|
|||||||
#[func]
|
#[func]
|
||||||
pub fn join(
|
pub fn join(
|
||||||
self,
|
self,
|
||||||
|
engine: &mut Engine,
|
||||||
|
span: Span,
|
||||||
/// 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>,
|
||||||
@ -701,13 +714,18 @@ impl Array {
|
|||||||
for (i, value) in self.into_iter().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(),
|
||||||
|
&mut (&mut *engine, span),
|
||||||
|
)?;
|
||||||
} else {
|
} else {
|
||||||
result = ops::join(result, separator.clone())?;
|
result =
|
||||||
|
ops::join(result, separator.clone(), &mut (&mut *engine, span))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result = ops::join(result, value)?;
|
result = ops::join(result, value, &mut (&mut *engine, span))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
@ -862,13 +880,14 @@ impl Array {
|
|||||||
self,
|
self,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
context: Tracked<Context>,
|
context: Tracked<Context>,
|
||||||
|
span: Span,
|
||||||
/// If given, applies this function to the elements in the array to
|
/// If given, applies this function to the elements in the array to
|
||||||
/// determine the keys to deduplicate by.
|
/// determine the keys to deduplicate by.
|
||||||
#[named]
|
#[named]
|
||||||
key: Option<Func>,
|
key: Option<Func>,
|
||||||
) -> SourceResult<Array> {
|
) -> SourceResult<Array> {
|
||||||
let mut out = EcoVec::with_capacity(self.0.len());
|
let mut out = EcoVec::with_capacity(self.0.len());
|
||||||
let mut key_of = |x: Value| match &key {
|
let key_of = |engine: &mut Engine, 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`.
|
||||||
Some(f) => f.call(engine, context, [x]),
|
Some(f) => f.call(engine, context, [x]),
|
||||||
@ -879,14 +898,18 @@ impl Array {
|
|||||||
// 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 {
|
'outer: for value in self {
|
||||||
let key = key_of(value.clone())?;
|
let key = key_of(&mut *engine, value.clone())?;
|
||||||
if out.is_empty() {
|
if out.is_empty() {
|
||||||
out.push(value);
|
out.push(value);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for second in out.iter() {
|
for second in out.iter() {
|
||||||
if ops::equal(&key, &key_of(second.clone())?) {
|
if ops::equal(
|
||||||
|
&key,
|
||||||
|
&key_of(&mut *engine, second.clone())?,
|
||||||
|
&mut (&mut *engine, span),
|
||||||
|
) {
|
||||||
continue 'outer;
|
continue 'outer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ use std::cmp::Ordering;
|
|||||||
use ecow::eco_format;
|
use ecow::eco_format;
|
||||||
use typst_utils::Numeric;
|
use typst_utils::Numeric;
|
||||||
|
|
||||||
use crate::diag::{bail, HintedStrResult, StrResult};
|
use crate::diag::{bail, DeprecationSink, HintedStrResult, StrResult};
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
format_str, Datetime, IntoValue, Regex, Repr, SymbolElem, Value,
|
format_str, Datetime, IntoValue, Regex, Repr, SymbolElem, Value,
|
||||||
};
|
};
|
||||||
@ -21,7 +21,7 @@ macro_rules! mismatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Join a value with another value.
|
/// Join a value with another value.
|
||||||
pub fn join(lhs: Value, rhs: Value) -> StrResult<Value> {
|
pub fn join(lhs: Value, rhs: Value, sink: &mut dyn DeprecationSink) -> StrResult<Value> {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
Ok(match (lhs, rhs) {
|
Ok(match (lhs, rhs) {
|
||||||
(a, None) => a,
|
(a, None) => a,
|
||||||
@ -39,6 +39,17 @@ pub fn join(lhs: Value, rhs: Value) -> StrResult<Value> {
|
|||||||
(Array(a), Array(b)) => Array(a + b),
|
(Array(a), Array(b)) => Array(a + b),
|
||||||
(Dict(a), Dict(b)) => Dict(a + b),
|
(Dict(a), Dict(b)) => Dict(a + b),
|
||||||
(Args(a), Args(b)) => Args(a + b),
|
(Args(a), Args(b)) => Args(a + b),
|
||||||
|
|
||||||
|
// Type compatibility.
|
||||||
|
(Type(a), Str(b)) => {
|
||||||
|
warn_type_str_join(sink);
|
||||||
|
Str(format_str!("{a}{b}"))
|
||||||
|
}
|
||||||
|
(Str(a), Type(b)) => {
|
||||||
|
warn_type_str_join(sink);
|
||||||
|
Str(format_str!("{a}{b}"))
|
||||||
|
}
|
||||||
|
|
||||||
(a, b) => mismatch!("cannot join {} with {}", a, b),
|
(a, b) => mismatch!("cannot join {} with {}", a, b),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -88,7 +99,11 @@ pub fn neg(value: Value) -> HintedStrResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the sum of two values.
|
/// Compute the sum of two values.
|
||||||
pub fn add(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
|
pub fn add(
|
||||||
|
lhs: Value,
|
||||||
|
rhs: Value,
|
||||||
|
sink: &mut dyn DeprecationSink,
|
||||||
|
) -> HintedStrResult<Value> {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
Ok(match (lhs, rhs) {
|
Ok(match (lhs, rhs) {
|
||||||
(a, None) => a,
|
(a, None) => a,
|
||||||
@ -156,6 +171,16 @@ pub fn add(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
|
|||||||
(Datetime(a), Duration(b)) => Datetime(a + b),
|
(Datetime(a), Duration(b)) => Datetime(a + b),
|
||||||
(Duration(a), Datetime(b)) => Datetime(b + a),
|
(Duration(a), Datetime(b)) => Datetime(b + a),
|
||||||
|
|
||||||
|
// Type compatibility.
|
||||||
|
(Type(a), Str(b)) => {
|
||||||
|
warn_type_str_add(sink);
|
||||||
|
Str(format_str!("{a}{b}"))
|
||||||
|
}
|
||||||
|
(Str(a), Type(b)) => {
|
||||||
|
warn_type_str_add(sink);
|
||||||
|
Str(format_str!("{a}{b}"))
|
||||||
|
}
|
||||||
|
|
||||||
(Dyn(a), Dyn(b)) => {
|
(Dyn(a), Dyn(b)) => {
|
||||||
// Alignments can be summed.
|
// Alignments can be summed.
|
||||||
if let (Some(&a), Some(&b)) =
|
if let (Some(&a), Some(&b)) =
|
||||||
@ -394,13 +419,21 @@ pub fn or(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Compute whether two values are equal.
|
/// Compute whether two values are equal.
|
||||||
pub fn eq(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
|
pub fn eq(
|
||||||
Ok(Value::Bool(equal(&lhs, &rhs)))
|
lhs: Value,
|
||||||
|
rhs: Value,
|
||||||
|
sink: &mut dyn DeprecationSink,
|
||||||
|
) -> HintedStrResult<Value> {
|
||||||
|
Ok(Value::Bool(equal(&lhs, &rhs, sink)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute whether two values are unequal.
|
/// Compute whether two values are unequal.
|
||||||
pub fn neq(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
|
pub fn neq(
|
||||||
Ok(Value::Bool(!equal(&lhs, &rhs)))
|
lhs: Value,
|
||||||
|
rhs: Value,
|
||||||
|
sink: &mut dyn DeprecationSink,
|
||||||
|
) -> HintedStrResult<Value> {
|
||||||
|
Ok(Value::Bool(!equal(&lhs, &rhs, sink)))
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! comparison {
|
macro_rules! comparison {
|
||||||
@ -419,7 +452,7 @@ comparison!(gt, ">", Ordering::Greater);
|
|||||||
comparison!(geq, ">=", Ordering::Greater | Ordering::Equal);
|
comparison!(geq, ">=", Ordering::Greater | Ordering::Equal);
|
||||||
|
|
||||||
/// Determine whether two values are equal.
|
/// Determine whether two values are equal.
|
||||||
pub fn equal(lhs: &Value, rhs: &Value) -> bool {
|
pub fn equal(lhs: &Value, rhs: &Value, sink: &mut dyn DeprecationSink) -> bool {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
match (lhs, rhs) {
|
match (lhs, rhs) {
|
||||||
// Compare reflexively.
|
// Compare reflexively.
|
||||||
@ -463,6 +496,12 @@ pub fn equal(lhs: &Value, rhs: &Value) -> bool {
|
|||||||
rat == rel.rel && rel.abs.is_zero()
|
rat == rel.rel && rel.abs.is_zero()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type compatibility.
|
||||||
|
(Type(ty), Str(str)) | (Str(str), Type(ty)) => {
|
||||||
|
warn_type_str_equal(sink);
|
||||||
|
ty.compat_name() == str.as_str()
|
||||||
|
}
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -534,8 +573,12 @@ fn try_cmp_arrays(a: &[Value], b: &[Value]) -> StrResult<Ordering> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Test whether one value is "in" another one.
|
/// Test whether one value is "in" another one.
|
||||||
pub fn in_(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
|
pub fn in_(
|
||||||
if let Some(b) = contains(&lhs, &rhs) {
|
lhs: Value,
|
||||||
|
rhs: Value,
|
||||||
|
sink: &mut dyn DeprecationSink,
|
||||||
|
) -> HintedStrResult<Value> {
|
||||||
|
if let Some(b) = contains(&lhs, &rhs, sink) {
|
||||||
Ok(Value::Bool(b))
|
Ok(Value::Bool(b))
|
||||||
} else {
|
} else {
|
||||||
mismatch!("cannot apply 'in' to {} and {}", lhs, rhs)
|
mismatch!("cannot apply 'in' to {} and {}", lhs, rhs)
|
||||||
@ -543,8 +586,12 @@ pub fn in_(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Test whether one value is "not in" another one.
|
/// Test whether one value is "not in" another one.
|
||||||
pub fn not_in(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
|
pub fn not_in(
|
||||||
if let Some(b) = contains(&lhs, &rhs) {
|
lhs: Value,
|
||||||
|
rhs: Value,
|
||||||
|
sink: &mut dyn DeprecationSink,
|
||||||
|
) -> HintedStrResult<Value> {
|
||||||
|
if let Some(b) = contains(&lhs, &rhs, sink) {
|
||||||
Ok(Value::Bool(!b))
|
Ok(Value::Bool(!b))
|
||||||
} else {
|
} else {
|
||||||
mismatch!("cannot apply 'not in' to {} and {}", lhs, rhs)
|
mismatch!("cannot apply 'not in' to {} and {}", lhs, rhs)
|
||||||
@ -552,13 +599,27 @@ pub fn not_in(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Test for containment.
|
/// Test for containment.
|
||||||
pub fn contains(lhs: &Value, rhs: &Value) -> Option<bool> {
|
pub fn contains(
|
||||||
|
lhs: &Value,
|
||||||
|
rhs: &Value,
|
||||||
|
sink: &mut dyn DeprecationSink,
|
||||||
|
) -> Option<bool> {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
match (lhs, rhs) {
|
match (lhs, rhs) {
|
||||||
(Str(a), Str(b)) => Some(b.as_str().contains(a.as_str())),
|
(Str(a), Str(b)) => Some(b.as_str().contains(a.as_str())),
|
||||||
(Dyn(a), Str(b)) => a.downcast::<Regex>().map(|regex| regex.is_match(b)),
|
(Dyn(a), Str(b)) => a.downcast::<Regex>().map(|regex| regex.is_match(b)),
|
||||||
(Str(a), Dict(b)) => Some(b.contains(a)),
|
(Str(a), Dict(b)) => Some(b.contains(a)),
|
||||||
(a, Array(b)) => Some(b.contains(a.clone())),
|
(a, Array(b)) => Some(b.contains_impl(a, sink)),
|
||||||
|
|
||||||
|
// Type compatibility.
|
||||||
|
(Type(a), Str(b)) => {
|
||||||
|
warn_type_in_str(sink);
|
||||||
|
Some(b.as_str().contains(a.compat_name()))
|
||||||
|
}
|
||||||
|
(Type(a), Dict(b)) => {
|
||||||
|
warn_type_in_dict(sink);
|
||||||
|
Some(b.contains(a.compat_name()))
|
||||||
|
}
|
||||||
|
|
||||||
_ => Option::None,
|
_ => Option::None,
|
||||||
}
|
}
|
||||||
@ -568,3 +629,46 @@ pub fn contains(lhs: &Value, rhs: &Value) -> Option<bool> {
|
|||||||
fn too_large() -> &'static str {
|
fn too_large() -> &'static str {
|
||||||
"value is too large"
|
"value is too large"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cold]
|
||||||
|
fn warn_type_str_add(sink: &mut dyn DeprecationSink) {
|
||||||
|
sink.emit_with_hints(
|
||||||
|
"adding strings and types is deprecated",
|
||||||
|
&["convert the type to a string with `str` first"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cold]
|
||||||
|
fn warn_type_str_join(sink: &mut dyn DeprecationSink) {
|
||||||
|
sink.emit_with_hints(
|
||||||
|
"joining strings and types is deprecated",
|
||||||
|
&["convert the type to a string with `str` first"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cold]
|
||||||
|
fn warn_type_str_equal(sink: &mut dyn DeprecationSink) {
|
||||||
|
sink.emit_with_hints(
|
||||||
|
"comparing strings with types is deprecated",
|
||||||
|
&[
|
||||||
|
"compare with the literal type instead",
|
||||||
|
"this comparison will always return `false` in future Typst releases",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cold]
|
||||||
|
fn warn_type_in_str(sink: &mut dyn DeprecationSink) {
|
||||||
|
sink.emit_with_hints(
|
||||||
|
"checking whether a type is contained in a string is deprecated",
|
||||||
|
&["this compatibility behavior only exists because `type` used to return a string"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cold]
|
||||||
|
fn warn_type_in_dict(sink: &mut dyn DeprecationSink) {
|
||||||
|
sink.emit_with_hints(
|
||||||
|
"checking whether a type is contained in a dictionary is deprecated",
|
||||||
|
&["this compatibility behavior only exists because `type` used to return a string"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -300,7 +300,7 @@ impl Binding {
|
|||||||
/// As the `sink`
|
/// As the `sink`
|
||||||
/// - pass `()` to ignore the message.
|
/// - pass `()` to ignore the message.
|
||||||
/// - pass `(&mut engine, span)` to emit a warning into the engine.
|
/// - pass `(&mut engine, span)` to emit a warning into the engine.
|
||||||
pub fn read_checked(&self, sink: impl DeprecationSink) -> &Value {
|
pub fn read_checked(&self, mut sink: impl DeprecationSink) -> &Value {
|
||||||
if let Some(message) = self.deprecation {
|
if let Some(message) = self.deprecation {
|
||||||
sink.emit(message);
|
sink.emit(message);
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,16 @@ use crate::foundations::{
|
|||||||
/// #type(int) \
|
/// #type(int) \
|
||||||
/// #type(type)
|
/// #type(type)
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Compatibility
|
||||||
|
/// In Typst 0.7 and lower, the `type` function returned a string instead of a
|
||||||
|
/// type. Compatibility with the old way will remain until Typst 0.14 to give
|
||||||
|
/// package authors time to upgrade.
|
||||||
|
///
|
||||||
|
/// - Checks like `{int == "integer"}` evaluate to `{true}`
|
||||||
|
/// - Adding/joining a type and string will yield a string
|
||||||
|
/// - The `{in}` operator on a type and a dictionary will evaluate to `{true}`
|
||||||
|
/// if the dictionary has a string key matching the type's name
|
||||||
#[ty(scope, cast)]
|
#[ty(scope, cast)]
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct Type(Static<NativeTypeData>);
|
pub struct Type(Static<NativeTypeData>);
|
||||||
@ -106,6 +116,14 @@ impl Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type compatibility.
|
||||||
|
impl Type {
|
||||||
|
/// The type's backward-compatible name.
|
||||||
|
pub fn compat_name(&self) -> &str {
|
||||||
|
self.long_name()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[scope]
|
#[scope]
|
||||||
impl Type {
|
impl Type {
|
||||||
/// Determines a value's type.
|
/// Determines a value's type.
|
||||||
|
@ -292,7 +292,8 @@ impl Repr for Value {
|
|||||||
|
|
||||||
impl PartialEq for Value {
|
impl PartialEq for Value {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
ops::equal(self, other)
|
// No way to emit deprecation warnings here :(
|
||||||
|
ops::equal(self, other, &mut ())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,6 +313,9 @@ feature flag.
|
|||||||
functions directly accepting both paths and bytes
|
functions directly accepting both paths and bytes
|
||||||
- The `sect` and its variants in favor of `inter`, and `integral.sect` in favor
|
- The `sect` and its variants in favor of `inter`, and `integral.sect` in favor
|
||||||
of `integral.inter`
|
of `integral.inter`
|
||||||
|
- The compatibility behavior of type/str comparisons (e.g. `{int == "integer"}`)
|
||||||
|
which was temporarily introduced in Typst 0.8 now emits warnings. It will be
|
||||||
|
removed in Typst 0.14.
|
||||||
|
|
||||||
## Removals
|
## Removals
|
||||||
- Removed `style` function and `styles` argument of [`measure`], use a [context]
|
- Removed `style` function and `styles` argument of [`measure`], use a [context]
|
||||||
@ -324,9 +327,6 @@ feature flag.
|
|||||||
- Removed compatibility behavior where [`counter.display`] worked without
|
- Removed compatibility behavior where [`counter.display`] worked without
|
||||||
[context] **(Breaking change)**
|
[context] **(Breaking change)**
|
||||||
- Removed compatibility behavior of [`locate`] **(Breaking change)**
|
- Removed compatibility behavior of [`locate`] **(Breaking change)**
|
||||||
- Removed compatibility behavior of type/str comparisons
|
|
||||||
(e.g. `{int == "integer"}`) which was temporarily introduced in Typst 0.8
|
|
||||||
**(Breaking change)**
|
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
- The `typst::compile` function is now generic and can return either a
|
- The `typst::compile` function is now generic and can return either a
|
||||||
|
@ -251,6 +251,6 @@ fn lines(
|
|||||||
(1..=count)
|
(1..=count)
|
||||||
.map(|n| numbering.apply(engine, context, &[n]))
|
.map(|n| numbering.apply(engine, context, &[n]))
|
||||||
.collect::<SourceResult<Array>>()?
|
.collect::<SourceResult<Array>>()?
|
||||||
.join(Some('\n'.into_value()), None)
|
.join(engine, span, Some('\n'.into_value()), None)
|
||||||
.at(span)
|
.at(span)
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,60 @@
|
|||||||
#test(type(1), int)
|
#test(type(1), int)
|
||||||
#test(type(ltr), direction)
|
#test(type(ltr), direction)
|
||||||
#test(type(10 / 3), float)
|
#test(type(10 / 3), float)
|
||||||
|
#test(type(10) == int, true)
|
||||||
|
#test(type(10) != int, false)
|
||||||
|
|
||||||
|
--- type-string-compatibility-add ---
|
||||||
|
// Warning: 7-23 adding strings and types is deprecated
|
||||||
|
// Hint: 7-23 convert the type to a string with `str` first
|
||||||
|
#test("is " + type(10), "is integer")
|
||||||
|
// Warning: 7-23 adding strings and types is deprecated
|
||||||
|
// Hint: 7-23 convert the type to a string with `str` first
|
||||||
|
#test(type(10) + " is", "integer is")
|
||||||
|
|
||||||
|
--- type-string-compatibility-join ---
|
||||||
|
// Warning: 16-24 joining strings and types is deprecated
|
||||||
|
// Hint: 16-24 convert the type to a string with `str` first
|
||||||
|
#test({ "is "; type(10) }, "is integer")
|
||||||
|
// Warning: 19-24 joining strings and types is deprecated
|
||||||
|
// Hint: 19-24 convert the type to a string with `str` first
|
||||||
|
#test({ type(10); " is" }, "integer is")
|
||||||
|
|
||||||
|
--- type-string-compatibility-equal ---
|
||||||
|
// Warning: 7-28 comparing strings with types is deprecated
|
||||||
|
// Hint: 7-28 compare with the literal type instead
|
||||||
|
// Hint: 7-28 this comparison will always return `false` in future Typst releases
|
||||||
|
#test(type(10) == "integer", true)
|
||||||
|
// Warning: 7-26 comparing strings with types is deprecated
|
||||||
|
// Hint: 7-26 compare with the literal type instead
|
||||||
|
// Hint: 7-26 this comparison will always return `false` in future Typst releases
|
||||||
|
#test(type(10) != "float", true)
|
||||||
|
|
||||||
|
--- type-string-compatibility-in-array ---
|
||||||
|
// Warning: 7-35 comparing strings with types is deprecated
|
||||||
|
// Hint: 7-35 compare with the literal type instead
|
||||||
|
// Hint: 7-35 this comparison will always return `false` in future Typst releases
|
||||||
|
#test(int in ("integer", "string"), true)
|
||||||
|
// Warning: 7-37 comparing strings with types is deprecated
|
||||||
|
// Hint: 7-37 compare with the literal type instead
|
||||||
|
// Hint: 7-37 this comparison will always return `false` in future Typst releases
|
||||||
|
#test(float in ("integer", "string"), false)
|
||||||
|
|
||||||
|
--- type-string-compatibility-in-str ---
|
||||||
|
// Warning: 7-35 checking whether a type is contained in a string is deprecated
|
||||||
|
// Hint: 7-35 this compatibility behavior only exists because `type` used to return a string
|
||||||
|
#test(int in "integers or strings", true)
|
||||||
|
// Warning: 7-35 checking whether a type is contained in a string is deprecated
|
||||||
|
// Hint: 7-35 this compatibility behavior only exists because `type` used to return a string
|
||||||
|
#test(str in "integers or strings", true)
|
||||||
|
// Warning: 7-37 checking whether a type is contained in a string is deprecated
|
||||||
|
// Hint: 7-37 this compatibility behavior only exists because `type` used to return a string
|
||||||
|
#test(float in "integers or strings", false)
|
||||||
|
|
||||||
|
--- type-string-compatibility-in-dict ---
|
||||||
|
// Warning: 7-37 checking whether a type is contained in a dictionary is deprecated
|
||||||
|
// Hint: 7-37 this compatibility behavior only exists because `type` used to return a string
|
||||||
|
#test(int in (integer: 1, string: 2), true)
|
||||||
|
|
||||||
--- issue-3110-type-constructor ---
|
--- issue-3110-type-constructor ---
|
||||||
// Let the error message report the type name.
|
// Let the error message report the type name.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user