From bca035172c463e6ac4aaf2591d7d4af2da51c522 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Fri, 18 Jun 2021 11:59:05 +0200 Subject: [PATCH] Join semantics --- src/eval/mod.rs | 30 ++++++++++-------------------- src/eval/ops.rs | 29 +++++++++++++++++++++++++++-- src/eval/value.rs | 29 +++++++++++++++++++++-------- tests/ref/code/block.png | Bin 1407 -> 3152 bytes tests/typ/code/block-invalid.typ | 5 +++-- tests/typ/code/block.typ | 28 ++++++++++++++++++++++++---- tests/typ/code/for.typ | 31 ++++++++++++++++--------------- tests/typ/code/while.typ | 10 ++++++---- tests/typeset.rs | 4 +--- 9 files changed, 108 insertions(+), 58 deletions(-) diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 67f710c31..cf02d2a5f 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 b6bd5402c..2537f4a5f 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 { + 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 f2bc4fd66..b29d01f32 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(self) -> CastResult - where - T: Cast, - { - 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(self) -> CastResult + where + T: Cast, + { + 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 6b0dd540a374ca438249d3466404863f5e8a4633..3e64d82c528ebfa5c5d84c8c81479184928131a1 100644 GIT binary patch literal 3152 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 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 e55f8f108..306c1e450 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 42151ac60..f4e74bc10 100644 --- a/tests/typeset.rs +++ b/tests/typeset.rs @@ -327,10 +327,8 @@ fn register_helpers(scope: &mut Scope, panics: Rc>>) { let rhs = args.expect::(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);