mirror of
https://github.com/typst/typst
synced 2025-06-28 08:12:53 +08:00
Join semantics
This commit is contained in:
parent
8b6391040e
commit
bca035172c
@ -327,7 +327,8 @@ impl Eval for BlockExpr {
|
|||||||
|
|
||||||
let mut output = Value::None;
|
let mut output = Value::None;
|
||||||
for expr in &self.exprs {
|
for expr in &self.exprs {
|
||||||
output = expr.eval(ctx);
|
let value = expr.eval(ctx);
|
||||||
|
output = output.join(ctx, value, expr.span());
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.scoping {
|
if self.scoping {
|
||||||
@ -584,19 +585,15 @@ impl Eval for WhileExpr {
|
|||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
|
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
|
||||||
let mut output = vec![];
|
let mut output = Value::None;
|
||||||
loop {
|
loop {
|
||||||
let condition = self.condition.eval(ctx);
|
let condition = self.condition.eval(ctx);
|
||||||
if let Some(condition) = ctx.cast(condition, self.condition.span()) {
|
if let Some(condition) = ctx.cast(condition, self.condition.span()) {
|
||||||
if condition {
|
if condition {
|
||||||
match self.body.eval(ctx) {
|
let value = self.body.eval(ctx);
|
||||||
Value::Template(v) => output.extend(v),
|
output = output.join(ctx, value, self.body.span());
|
||||||
Value::Str(v) => output.push(TemplateNode::Str(v)),
|
|
||||||
Value::Error => return Value::Error,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return Value::Template(output);
|
return output;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Value::Error;
|
return Value::Error;
|
||||||
@ -611,26 +608,19 @@ impl Eval for ForExpr {
|
|||||||
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
|
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
|
||||||
macro_rules! iter {
|
macro_rules! iter {
|
||||||
(for ($($binding:ident => $value:ident),*) in $iter:expr) => {{
|
(for ($($binding:ident => $value:ident),*) in $iter:expr) => {{
|
||||||
let mut output = vec![];
|
let mut output = Value::None;
|
||||||
ctx.scopes.enter();
|
ctx.scopes.enter();
|
||||||
|
|
||||||
#[allow(unused_parens)]
|
#[allow(unused_parens)]
|
||||||
for ($($value),*) in $iter {
|
for ($($value),*) in $iter {
|
||||||
$(ctx.scopes.def_mut($binding.as_str(), $value);)*
|
$(ctx.scopes.def_mut($binding.as_str(), $value);)*
|
||||||
|
|
||||||
match self.body.eval(ctx) {
|
let value = self.body.eval(ctx);
|
||||||
Value::Template(v) => output.extend(v),
|
output = output.join(ctx, value, self.body.span());
|
||||||
Value::Str(v) => output.push(TemplateNode::Str(v)),
|
|
||||||
Value::Error => {
|
|
||||||
ctx.scopes.exit();
|
|
||||||
return Value::Error;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.scopes.exit();
|
ctx.scopes.exit();
|
||||||
Value::Template(output)
|
output
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,33 @@ use std::cmp::Ordering::*;
|
|||||||
use super::{TemplateNode, Value};
|
use super::{TemplateNode, Value};
|
||||||
use Value::*;
|
use Value::*;
|
||||||
|
|
||||||
|
/// Join a value with another value.
|
||||||
|
pub fn join(lhs: Value, rhs: Value) -> Result<Value, Value> {
|
||||||
|
Ok(match (lhs, rhs) {
|
||||||
|
(_, Error) => Error,
|
||||||
|
(Error, _) => Error,
|
||||||
|
|
||||||
|
(a, None) => a,
|
||||||
|
(None, b) => b,
|
||||||
|
|
||||||
|
(Str(a), Str(b)) => Str(a + &b),
|
||||||
|
(Array(a), Array(b)) => Array(concat(a, b)),
|
||||||
|
(Dict(a), Dict(b)) => Dict(concat(a, b)),
|
||||||
|
|
||||||
|
(Template(a), Template(b)) => Template(concat(a, b)),
|
||||||
|
(Template(mut a), Str(b)) => Template({
|
||||||
|
a.push(TemplateNode::Str(b));
|
||||||
|
a
|
||||||
|
}),
|
||||||
|
(Str(a), Template(mut b)) => Template({
|
||||||
|
b.insert(0, TemplateNode::Str(a));
|
||||||
|
b
|
||||||
|
}),
|
||||||
|
|
||||||
|
(a, _) => return Err(a),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Apply the plus operator to a value.
|
/// Apply the plus operator to a value.
|
||||||
pub fn pos(value: Value) -> Value {
|
pub fn pos(value: Value) -> Value {
|
||||||
match value {
|
match value {
|
||||||
@ -60,8 +87,6 @@ pub fn add(lhs: Value, rhs: Value) -> Value {
|
|||||||
(Dict(a), Dict(b)) => Dict(concat(a, b)),
|
(Dict(a), Dict(b)) => Dict(concat(a, b)),
|
||||||
|
|
||||||
(Template(a), Template(b)) => Template(concat(a, b)),
|
(Template(a), Template(b)) => Template(concat(a, b)),
|
||||||
(Template(a), None) => Template(a),
|
|
||||||
(None, Template(b)) => Template(b),
|
|
||||||
(Template(mut a), Str(b)) => Template({
|
(Template(mut a), Str(b)) => Template({
|
||||||
a.push(TemplateNode::Str(b));
|
a.push(TemplateNode::Str(b));
|
||||||
a
|
a
|
||||||
|
@ -5,6 +5,7 @@ use std::fmt::{self, Debug, Display, Formatter};
|
|||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use super::ops;
|
||||||
use super::EvalContext;
|
use super::EvalContext;
|
||||||
use crate::color::{Color, RgbaColor};
|
use crate::color::{Color, RgbaColor};
|
||||||
use crate::exec::ExecContext;
|
use crate::exec::ExecContext;
|
||||||
@ -61,14 +62,6 @@ impl Value {
|
|||||||
Self::Template(vec![TemplateNode::Func(TemplateFunc::new(name, f))])
|
Self::Template(vec![TemplateNode::Func(TemplateFunc::new(name, f))])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to cast the value into a specific type.
|
|
||||||
pub fn cast<T>(self) -> CastResult<T, Self>
|
|
||||||
where
|
|
||||||
T: Cast<Value>,
|
|
||||||
{
|
|
||||||
T::cast(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The name of the stored value's type.
|
/// The name of the stored value's type.
|
||||||
pub fn type_name(&self) -> &'static str {
|
pub fn type_name(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
@ -125,6 +118,26 @@ impl Value {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Try to cast the value into a specific type.
|
||||||
|
pub fn cast<T>(self) -> CastResult<T, Self>
|
||||||
|
where
|
||||||
|
T: Cast<Value>,
|
||||||
|
{
|
||||||
|
T::cast(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Join with another value.
|
||||||
|
pub fn join(self, ctx: &mut EvalContext, other: Self, span: Span) -> Self {
|
||||||
|
let (lhs, rhs) = (self.type_name(), other.type_name());
|
||||||
|
match ops::join(self, other) {
|
||||||
|
Ok(joined) => joined,
|
||||||
|
Err(prev) => {
|
||||||
|
ctx.diag(error!(span, "cannot join {} with {}", lhs, rhs));
|
||||||
|
prev
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Value {
|
impl Default for Value {
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 3.1 KiB |
@ -7,8 +7,9 @@
|
|||||||
{1u}
|
{1u}
|
||||||
|
|
||||||
// Should output `1`.
|
// Should output `1`.
|
||||||
// Error: 3 expected semicolon or line break
|
// Error: 2:3 expected semicolon or line break
|
||||||
{0 1}
|
// Error: 1:4-1:5 cannot join integer with integer
|
||||||
|
{1 2}
|
||||||
|
|
||||||
// Should output `2`.
|
// Should output `2`.
|
||||||
// Error: 2:12 expected semicolon or line break
|
// Error: 2:12 expected semicolon or line break
|
||||||
|
@ -9,7 +9,7 @@ All none
|
|||||||
// Let evaluates to none.
|
// Let evaluates to none.
|
||||||
{ let v = 0 }
|
{ let v = 0 }
|
||||||
|
|
||||||
// Trailing none evaluates to none.
|
// Type is joined with trailing none, evaluates to string.
|
||||||
{
|
{
|
||||||
type("")
|
type("")
|
||||||
none
|
none
|
||||||
@ -19,15 +19,35 @@ All none
|
|||||||
// Evaluates to single expression.
|
// Evaluates to single expression.
|
||||||
{ "Hello" }
|
{ "Hello" }
|
||||||
|
|
||||||
// Evaluates to trailing expression.
|
// Evaluates to string.
|
||||||
{ let x = "Hel"; x + "lo" }
|
{ let x = "Hel"; x + "lo" }
|
||||||
|
|
||||||
// Evaluates to concatenation of for loop bodies.
|
// Evaluates to join of none, [He] and the two loop bodies.
|
||||||
{
|
{
|
||||||
let parts = ("Hel", "lo")
|
let parts = ("l", "lo")
|
||||||
|
[He]
|
||||||
for s in parts [{s}]
|
for s in parts [{s}]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
// Evaluates to join of the templates and strings.
|
||||||
|
{
|
||||||
|
[Hey, ]
|
||||||
|
if true {
|
||||||
|
"there!"
|
||||||
|
}
|
||||||
|
[ ]
|
||||||
|
if false [Nope]
|
||||||
|
[How are ] + "you?"
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
[A]
|
||||||
|
// Error: 5-6 cannot join template with integer
|
||||||
|
1
|
||||||
|
[B]
|
||||||
|
}
|
||||||
|
|
||||||
---
|
---
|
||||||
// Works the same way in code environment.
|
// Works the same way in code environment.
|
||||||
// Ref: false
|
// Ref: false
|
||||||
|
@ -20,14 +20,13 @@
|
|||||||
|
|
||||||
// String.
|
// String.
|
||||||
{
|
{
|
||||||
let out = ""
|
|
||||||
let first = true
|
let first = true
|
||||||
for c in "abc" {
|
let out = for c in "abc" {
|
||||||
if not first {
|
if not first {
|
||||||
out += ", "
|
", "
|
||||||
}
|
}
|
||||||
|
c
|
||||||
first = false
|
first = false
|
||||||
out += c
|
|
||||||
}
|
}
|
||||||
test(out, "a, b, c")
|
test(out, "a, b, c")
|
||||||
}
|
}
|
||||||
@ -36,14 +35,16 @@
|
|||||||
// Block body.
|
// Block body.
|
||||||
// Should output `[1st, 2nd, 3rd, 4th, 5th, 6th]`.
|
// Should output `[1st, 2nd, 3rd, 4th, 5th, 6th]`.
|
||||||
{
|
{
|
||||||
"[" + for v in (1, 2, 3, 4, 5, 6) {
|
"["
|
||||||
(if v > 1 [, ]
|
for v in (1, 2, 3, 4, 5, 6) {
|
||||||
+ [{v}]
|
if v > 1 [, ]
|
||||||
+ if v == 1 [st]
|
[#v]
|
||||||
+ if v == 2 [nd]
|
if v == 1 [st]
|
||||||
+ if v == 3 [rd]
|
if v == 2 [nd]
|
||||||
+ if v >= 4 [th])
|
if v == 3 [rd]
|
||||||
} + "]"
|
if v >= 4 [th]
|
||||||
|
}
|
||||||
|
"]"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Template body.
|
// Template body.
|
||||||
@ -53,8 +54,8 @@
|
|||||||
---
|
---
|
||||||
// Value of for loops.
|
// Value of for loops.
|
||||||
// Ref: false
|
// Ref: false
|
||||||
#test(type(for v in () {}), "template")
|
#test(for v in "" [], none)
|
||||||
#test(type(for v in () []), "template")
|
#test(type(for v in "1" []), "template")
|
||||||
|
|
||||||
---
|
---
|
||||||
// Ref: false
|
// Ref: false
|
||||||
@ -67,7 +68,7 @@
|
|||||||
// Error: 11-18 cannot add integer and string
|
// Error: 11-18 cannot add integer and string
|
||||||
#for v in 1 + "2" {}
|
#for v in 1 + "2" {}
|
||||||
|
|
||||||
// A single error stops iteration.
|
// Errors taint everything.
|
||||||
#test(error, for v in (1, 2, 3) {
|
#test(error, for v in (1, 2, 3) {
|
||||||
if v < 2 [Ok] else {error}
|
if v < 2 [Ok] else {error}
|
||||||
})
|
})
|
||||||
|
@ -23,8 +23,10 @@
|
|||||||
// Value of while loops.
|
// Value of while loops.
|
||||||
// Ref: false
|
// Ref: false
|
||||||
|
|
||||||
#test(type(while false {}), "template")
|
#test(while false {}, none)
|
||||||
#test(type(while false []), "template")
|
|
||||||
|
#let i = 0
|
||||||
|
#test(type(while i < 1 [{ i += 1 }]), "template")
|
||||||
|
|
||||||
---
|
---
|
||||||
// Ref: false
|
// Ref: false
|
||||||
@ -37,13 +39,13 @@
|
|||||||
// Error: 8-15 unknown variable
|
// Error: 8-15 unknown variable
|
||||||
#while nothing {}
|
#while nothing {}
|
||||||
|
|
||||||
// A single error stops iteration.
|
// Errors taint everything.
|
||||||
#let i = 0
|
#let i = 0
|
||||||
#test(error, while i < 10 {
|
#test(error, while i < 10 {
|
||||||
i += 1
|
i += 1
|
||||||
if i < 5 [nope] else { error }
|
if i < 5 [nope] else { error }
|
||||||
})
|
})
|
||||||
#test(i, 5)
|
#test(i, 10)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 7 expected expression
|
// Error: 7 expected expression
|
||||||
|
@ -327,10 +327,8 @@ fn register_helpers(scope: &mut Scope, panics: Rc<RefCell<Vec<Panic>>>) {
|
|||||||
let rhs = args.expect::<Value>(ctx, "right-hand side");
|
let rhs = args.expect::<Value>(ctx, "right-hand side");
|
||||||
if lhs != rhs {
|
if lhs != rhs {
|
||||||
panics.borrow_mut().push(Panic { pos: args.span.start, lhs, rhs });
|
panics.borrow_mut().push(Panic { pos: args.span.start, lhs, rhs });
|
||||||
Value::Str(format!("(panic)"))
|
|
||||||
} else {
|
|
||||||
Value::None
|
|
||||||
}
|
}
|
||||||
|
Value::None
|
||||||
};
|
};
|
||||||
|
|
||||||
scope.def_const("error", Value::Error);
|
scope.def_const("error", Value::Error);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user