summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/eval/mod.rs30
-rw-r--r--src/eval/ops.rs29
-rw-r--r--src/eval/value.rs29
-rw-r--r--tests/ref/code/block.pngbin1407 -> 3152 bytes
-rw-r--r--tests/typ/code/block-invalid.typ5
-rw-r--r--tests/typ/code/block.typ28
-rw-r--r--tests/typ/code/for.typ31
-rw-r--r--tests/typ/code/while.typ10
-rw-r--r--tests/typeset.rs4
9 files changed, 108 insertions, 58 deletions
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 67f710c3..cf02d2a5 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -327,7 +327,8 @@ impl Eval for BlockExpr {
let mut output = Value::None;
for expr in &self.exprs {
- output = expr.eval(ctx);
+ let value = expr.eval(ctx);
+ output = output.join(ctx, value, expr.span());
}
if self.scoping {
@@ -584,19 +585,15 @@ impl Eval for WhileExpr {
type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
- let mut output = vec![];
+ let mut output = Value::None;
loop {
let condition = self.condition.eval(ctx);
if let Some(condition) = ctx.cast(condition, self.condition.span()) {
if condition {
- match self.body.eval(ctx) {
- Value::Template(v) => output.extend(v),
- Value::Str(v) => output.push(TemplateNode::Str(v)),
- Value::Error => return Value::Error,
- _ => {}
- }
+ let value = self.body.eval(ctx);
+ output = output.join(ctx, value, self.body.span());
} else {
- return Value::Template(output);
+ return output;
}
} else {
return Value::Error;
@@ -611,26 +608,19 @@ impl Eval for ForExpr {
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
macro_rules! iter {
(for ($($binding:ident => $value:ident),*) in $iter:expr) => {{
- let mut output = vec![];
+ let mut output = Value::None;
ctx.scopes.enter();
#[allow(unused_parens)]
for ($($value),*) in $iter {
$(ctx.scopes.def_mut($binding.as_str(), $value);)*
- match self.body.eval(ctx) {
- Value::Template(v) => output.extend(v),
- Value::Str(v) => output.push(TemplateNode::Str(v)),
- Value::Error => {
- ctx.scopes.exit();
- return Value::Error;
- }
- _ => {}
- }
+ let value = self.body.eval(ctx);
+ output = output.join(ctx, value, self.body.span());
}
ctx.scopes.exit();
- Value::Template(output)
+ output
}};
}
diff --git a/src/eval/ops.rs b/src/eval/ops.rs
index b6bd5402..2537f4a5 100644
--- a/src/eval/ops.rs
+++ b/src/eval/ops.rs
@@ -3,6 +3,33 @@ use std::cmp::Ordering::*;
use super::{TemplateNode, 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.
pub fn pos(value: Value) -> Value {
match value {
@@ -60,8 +87,6 @@ pub fn add(lhs: Value, rhs: Value) -> Value {
(Dict(a), Dict(b)) => Dict(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({
a.push(TemplateNode::Str(b));
a
diff --git a/src/eval/value.rs b/src/eval/value.rs
index f2bc4fd6..b29d01f3 100644
--- a/src/eval/value.rs
+++ b/src/eval/value.rs
@@ -5,6 +5,7 @@ use std::fmt::{self, Debug, Display, Formatter};
use std::ops::Deref;
use std::rc::Rc;
+use super::ops;
use super::EvalContext;
use crate::color::{Color, RgbaColor};
use crate::exec::ExecContext;
@@ -61,14 +62,6 @@ impl Value {
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.
pub fn type_name(&self) -> &'static str {
match self {
@@ -125,6 +118,26 @@ impl Value {
_ => 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 {
diff --git a/tests/ref/code/block.png b/tests/ref/code/block.png
index 6b0dd540..3e64d82c 100644
--- a/tests/ref/code/block.png
+++ b/tests/ref/code/block.png
Binary files differ
diff --git a/tests/typ/code/block-invalid.typ b/tests/typ/code/block-invalid.typ
index d98bf06b..ba3f02d3 100644
--- a/tests/typ/code/block-invalid.typ
+++ b/tests/typ/code/block-invalid.typ
@@ -7,8 +7,9 @@
{1u}
// Should output `1`.
-// Error: 3 expected semicolon or line break
-{0 1}
+// Error: 2:3 expected semicolon or line break
+// Error: 1:4-1:5 cannot join integer with integer
+{1 2}
// Should output `2`.
// Error: 2:12 expected semicolon or line break
diff --git a/tests/typ/code/block.typ b/tests/typ/code/block.typ
index 196e6c14..8c30fa64 100644
--- a/tests/typ/code/block.typ
+++ b/tests/typ/code/block.typ
@@ -9,7 +9,7 @@ All none
// Let evaluates to none.
{ let v = 0 }
-// Trailing none evaluates to none.
+// Type is joined with trailing none, evaluates to string.
{
type("")
none
@@ -19,16 +19,36 @@ All none
// Evaluates to single expression.
{ "Hello" }
-// Evaluates to trailing expression.
+// Evaluates to string.
{ 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}]
}
---
+// 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.
// Ref: false
#test(3, {
diff --git a/tests/typ/code/for.typ b/tests/typ/code/for.typ
index 321b08cf..e6bcf269 100644
--- a/tests/typ/code/for.typ
+++ b/tests/typ/code/for.typ
@@ -20,14 +20,13 @@
// String.
{
- let out = ""
let first = true
- for c in "abc" {
+ let out = for c in "abc" {
if not first {
- out += ", "
+ ", "
}
+ c
first = false
- out += c
}
test(out, "a, b, c")
}
@@ -36,14 +35,16 @@
// Block body.
// Should output `[1st, 2nd, 3rd, 4th, 5th, 6th]`.
{
- "[" + for v in (1, 2, 3, 4, 5, 6) {
- (if v > 1 [, ]
- + [{v}]
- + if v == 1 [st]
- + if v == 2 [nd]
- + if v == 3 [rd]
- + if v >= 4 [th])
- } + "]"
+ "["
+ for v in (1, 2, 3, 4, 5, 6) {
+ if v > 1 [, ]
+ [#v]
+ if v == 1 [st]
+ if v == 2 [nd]
+ if v == 3 [rd]
+ if v >= 4 [th]
+ }
+ "]"
}
// Template body.
@@ -53,8 +54,8 @@
---
// Value of for loops.
// Ref: false
-#test(type(for v in () {}), "template")
-#test(type(for v in () []), "template")
+#test(for v in "" [], none)
+#test(type(for v in "1" []), "template")
---
// Ref: false
@@ -67,7 +68,7 @@
// Error: 11-18 cannot add integer and string
#for v in 1 + "2" {}
-// A single error stops iteration.
+// Errors taint everything.
#test(error, for v in (1, 2, 3) {
if v < 2 [Ok] else {error}
})
diff --git a/tests/typ/code/while.typ b/tests/typ/code/while.typ
index e55f8f10..306c1e45 100644
--- a/tests/typ/code/while.typ
+++ b/tests/typ/code/while.typ
@@ -23,8 +23,10 @@
// Value of while loops.
// Ref: false
-#test(type(while false {}), "template")
-#test(type(while false []), "template")
+#test(while false {}, none)
+
+#let i = 0
+#test(type(while i < 1 [{ i += 1 }]), "template")
---
// Ref: false
@@ -37,13 +39,13 @@
// Error: 8-15 unknown variable
#while nothing {}
-// A single error stops iteration.
+// Errors taint everything.
#let i = 0
#test(error, while i < 10 {
i += 1
if i < 5 [nope] else { error }
})
-#test(i, 5)
+#test(i, 10)
---
// Error: 7 expected expression
diff --git a/tests/typeset.rs b/tests/typeset.rs
index 42151ac6..f4e74bc1 100644
--- a/tests/typeset.rs
+++ b/tests/typeset.rs
@@ -327,10 +327,8 @@ fn register_helpers(scope: &mut Scope, panics: Rc<RefCell<Vec<Panic>>>) {
let rhs = args.expect::<Value>(ctx, "right-hand side");
if 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);