summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNadrieril2020-03-18 21:37:14 +0000
committerNadrieril2020-03-31 21:44:01 +0100
commitfa89e9cb319b353332c9e835944e7f86a6604c42 (patch)
tree669c879e1231ce898dd83c786eb17976a2ba6171
parent7848c8e8d3147ebe428290558aa6c0efcbf59ee5 (diff)
Move Value, SimpleValue and SimpleType to serde
-rw-r--r--dhall/src/lib.rs66
-rw-r--r--dhall/src/semantics/tck/typecheck.rs5
-rw-r--r--dhall/src/simple.rs163
-rw-r--r--serde_dhall/src/lib.rs9
-rw-r--r--serde_dhall/src/serde.rs6
-rw-r--r--serde_dhall/src/simple.rs180
-rw-r--r--serde_dhall/src/static_type.rs3
-rw-r--r--serde_dhall/src/value.rs73
8 files changed, 257 insertions, 248 deletions
diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs
index 34a9bac..0f4b623 100644
--- a/dhall/src/lib.rs
+++ b/dhall/src/lib.rs
@@ -9,7 +9,6 @@ mod tests;
pub mod error;
pub mod semantics;
-pub mod simple;
pub mod syntax;
use std::fmt::Display;
@@ -23,8 +22,6 @@ use crate::semantics::resolve::ImportLocation;
use crate::semantics::{typecheck, typecheck_with, Hir, Nir, Tir, Type};
use crate::syntax::Expr;
-pub use crate::simple::{STyKind, SValKind, SimpleType, SimpleValue};
-
#[derive(Debug, Clone)]
pub struct Parsed(Expr, ImportLocation);
@@ -47,16 +44,6 @@ pub struct Typed {
#[derive(Debug, Clone)]
pub struct Normalized(Nir);
-/// A Dhall value.
-#[derive(Debug, Clone)]
-pub struct Value {
- /// Invariant: in normal form
- pub hir: Hir,
- /// Cached conversions because they are annoying to construct from Hir.
- pub as_simple_val: Option<SimpleValue>,
- pub as_simple_ty: Option<SimpleType>,
-}
-
/// Controls conversion from `Nir` to `Expr`
#[derive(Copy, Clone, Default)]
pub struct ToExprOptions {
@@ -131,14 +118,6 @@ impl Typed {
}
impl Normalized {
- pub fn to_value(&self) -> Value {
- Value {
- hir: self.to_hir(),
- as_simple_val: SimpleValue::from_nir(&self.0),
- as_simple_ty: SimpleType::from_nir(&self.0),
- }
- }
-
/// Converts a value back to the corresponding AST expression.
pub fn to_expr(&self) -> Expr {
self.0.to_expr(ToExprOptions::default())
@@ -147,42 +126,15 @@ impl Normalized {
pub fn to_hir(&self) -> Hir {
self.0.to_hir_noenv()
}
+ pub fn as_nir(&self) -> &Nir {
+ &self.0
+ }
/// Converts a value back to the corresponding AST expression, alpha-normalizing in the process.
pub fn to_expr_alpha(&self) -> Expr {
self.0.to_expr(ToExprOptions { alpha: true })
}
}
-impl Value {
- /// Parse a string into a Value, and optionally ensure that the value matches the provided type
- /// annotation.
- pub fn from_str_with_annot(
- s: &str,
- ty: Option<&Self>,
- ) -> Result<Self, Error> {
- let resolved = Parsed::parse_str(s)?.resolve()?;
- let typed = match ty {
- None => resolved.typecheck()?,
- Some(ty) => resolved.typecheck_with(&ty.hir)?,
- };
- Ok(typed.normalize().to_value())
- }
-
- /// Converts a Value into a SimpleValue.
- pub fn to_simple_value(&self) -> Option<SimpleValue> {
- self.as_simple_val.clone()
- }
- /// Converts a Value into a SimpleType.
- pub fn to_simple_type(&self) -> Option<SimpleType> {
- self.as_simple_ty.clone()
- }
-
- /// Converts a value back to the corresponding AST expression.
- pub fn to_expr(&self) -> Expr {
- self.hir.to_expr(ToExprOptions::default())
- }
-}
-
macro_rules! derive_traits_for_wrapper_struct {
($ty:ident) => {
impl std::cmp::PartialEq for $ty {
@@ -246,15 +198,3 @@ impl Display for Normalized {
self.to_expr().fmt(f)
}
}
-
-impl Eq for Value {}
-impl PartialEq for Value {
- fn eq(&self, other: &Self) -> bool {
- self.hir == other.hir
- }
-}
-impl Display for Value {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
- self.to_expr().fmt(f)
- }
-}
diff --git a/dhall/src/semantics/tck/typecheck.rs b/dhall/src/semantics/tck/typecheck.rs
index 205185f..c3334b5 100644
--- a/dhall/src/semantics/tck/typecheck.rs
+++ b/dhall/src/semantics/tck/typecheck.rs
@@ -57,10 +57,7 @@ pub fn mkerr<T, S: ToString>(msg: S) -> Result<T, TypeError> {
Err(TypeError::new(TypeMessage::Custom(msg.to_string())))
}
-pub fn mk_span_err<T, S: ToString>(
- span: Span,
- msg: S,
-) -> Result<T, TypeError> {
+pub fn mk_span_err<T, S: ToString>(span: Span, msg: S) -> Result<T, TypeError> {
mkerr(
ErrorBuilder::new(msg.to_string())
.span_err(span, msg.to_string())
diff --git a/dhall/src/simple.rs b/dhall/src/simple.rs
deleted file mode 100644
index c00380c..0000000
--- a/dhall/src/simple.rs
+++ /dev/null
@@ -1,163 +0,0 @@
-use std::collections::BTreeMap;
-
-use crate::semantics::{Hir, HirKind, Nir, NirKind};
-use crate::syntax::{Builtin, ExprKind, NumKind, Span};
-use crate::Value;
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct SimpleValue {
- kind: Box<SValKind>,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum SValKind {
- Num(NumKind),
- Text(String),
- Optional(Option<SimpleValue>),
- List(Vec<SimpleValue>),
- Record(BTreeMap<String, SimpleValue>),
- Union(String, Option<SimpleValue>),
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct SimpleType {
- kind: Box<STyKind>,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum STyKind {
- Bool,
- Natural,
- Integer,
- Double,
- Text,
- Optional(SimpleType),
- List(SimpleType),
- Record(BTreeMap<String, SimpleType>),
- Union(BTreeMap<String, Option<SimpleType>>),
-}
-
-impl SimpleValue {
- pub fn new(kind: SValKind) -> Self {
- SimpleValue {
- kind: Box::new(kind),
- }
- }
- pub fn from_nir(nir: &Nir) -> Option<Self> {
- Some(SimpleValue::new(match nir.kind() {
- NirKind::Num(lit) => SValKind::Num(lit.clone()),
- NirKind::TextLit(x) => SValKind::Text(
- x.as_text()
- .expect("Normal form should ensure the text is a string"),
- ),
- NirKind::EmptyOptionalLit(_) => SValKind::Optional(None),
- NirKind::NEOptionalLit(x) => {
- SValKind::Optional(Some(Self::from_nir(x)?))
- }
- NirKind::EmptyListLit(_) => SValKind::List(vec![]),
- NirKind::NEListLit(xs) => SValKind::List(
- xs.iter()
- .map(|v| Self::from_nir(v))
- .collect::<Option<_>>()?,
- ),
- NirKind::RecordLit(kvs) => SValKind::Record(
- kvs.iter()
- .map(|(k, v)| Some((k.into(), Self::from_nir(v)?)))
- .collect::<Option<_>>()?,
- ),
- NirKind::UnionLit(field, x, _) => {
- SValKind::Union(field.into(), Some(Self::from_nir(x)?))
- }
- NirKind::UnionConstructor(field, ty)
- if ty.get(field).map(|f| f.is_some()) == Some(false) =>
- {
- SValKind::Union(field.into(), None)
- }
- _ => return None,
- }))
- }
-
- pub fn kind(&self) -> &SValKind {
- self.kind.as_ref()
- }
-}
-
-impl SimpleType {
- pub fn new(kind: STyKind) -> Self {
- SimpleType {
- kind: Box::new(kind),
- }
- }
- pub fn from_nir(nir: &Nir) -> Option<Self> {
- Some(SimpleType::new(match nir.kind() {
- NirKind::BuiltinType(b) => match b {
- Builtin::Bool => STyKind::Bool,
- Builtin::Natural => STyKind::Natural,
- Builtin::Integer => STyKind::Integer,
- Builtin::Double => STyKind::Double,
- Builtin::Text => STyKind::Text,
- _ => unreachable!(),
- },
- NirKind::OptionalType(t) => STyKind::Optional(Self::from_nir(t)?),
- NirKind::ListType(t) => STyKind::List(Self::from_nir(t)?),
- NirKind::RecordType(kts) => STyKind::Record(
- kts.iter()
- .map(|(k, v)| Some((k.into(), Self::from_nir(v)?)))
- .collect::<Option<_>>()?,
- ),
- NirKind::UnionType(kts) => STyKind::Union(
- kts.iter()
- .map(|(k, v)| {
- Some((
- k.into(),
- v.as_ref()
- .map(|v| Ok(Self::from_nir(v)?))
- .transpose()?,
- ))
- })
- .collect::<Option<_>>()?,
- ),
- _ => return None,
- }))
- }
-
- pub fn kind(&self) -> &STyKind {
- self.kind.as_ref()
- }
- pub fn to_value(&self) -> Value {
- Value {
- hir: self.to_hir(),
- as_simple_val: None,
- as_simple_ty: Some(self.clone()),
- }
- }
- pub fn to_hir(&self) -> Hir {
- let hir = |k| Hir::new(HirKind::Expr(k), Span::Artificial);
- hir(match self.kind() {
- STyKind::Bool => ExprKind::Builtin(Builtin::Bool),
- STyKind::Natural => ExprKind::Builtin(Builtin::Natural),
- STyKind::Integer => ExprKind::Builtin(Builtin::Integer),
- STyKind::Double => ExprKind::Builtin(Builtin::Double),
- STyKind::Text => ExprKind::Builtin(Builtin::Text),
- STyKind::Optional(t) => ExprKind::App(
- hir(ExprKind::Builtin(Builtin::Optional)),
- t.to_hir(),
- ),
- STyKind::List(t) => {
- ExprKind::App(hir(ExprKind::Builtin(Builtin::List)), t.to_hir())
- }
- STyKind::Record(kts) => ExprKind::RecordType(
- kts.into_iter()
- .map(|(k, t)| (k.as_str().into(), t.to_hir()))
- .collect(),
- ),
- STyKind::Union(kts) => ExprKind::UnionType(
- kts.into_iter()
- .map(|(k, t)| {
- (k.as_str().into(), t.as_ref().map(|t| t.to_hir()))
- })
- .collect(),
- ),
- })
- }
-}
diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs
index 15f45ce..adc242f 100644
--- a/serde_dhall/src/lib.rs
+++ b/serde_dhall/src/lib.rs
@@ -171,11 +171,13 @@ mod error;
mod serde;
pub mod simple;
mod static_type;
+mod value;
#[doc(hidden)]
pub use dhall_proc_macros::StaticType;
pub use error::{Error, Result};
pub use static_type::StaticType;
+pub use value::Value;
use simple::Type;
@@ -191,16 +193,15 @@ pub(crate) mod sealed {
/// This trait cannot be implemented manually.
pub trait Deserialize: sealed::Sealed + Sized {
/// See [serde_dhall::from_str][crate::from_str]
- fn from_dhall(v: &dhall::Value) -> Result<Self>;
+ fn from_dhall(v: &Value) -> Result<Self>;
}
fn from_str_with_annot<T>(s: &str, ty: Option<&Type>) -> Result<T>
where
T: Deserialize,
{
- let ty = ty.map(|ty| ty.to_dhall_value());
- let val = dhall::Value::from_str_with_annot(s, ty.as_ref())
- .map_err(Error::Dhall)?;
+ let ty = ty.map(|ty| ty.to_value());
+ let val = Value::from_str_with_annot(s, ty.as_ref())?;
T::from_dhall(&val)
}
diff --git a/serde_dhall/src/serde.rs b/serde_dhall/src/serde.rs
index d144abf..91ef5d7 100644
--- a/serde_dhall/src/serde.rs
+++ b/serde_dhall/src/serde.rs
@@ -5,9 +5,9 @@ use serde::de::value::{
};
use dhall::syntax::NumKind;
-use dhall::{SValKind, SimpleValue};
-use crate::{Deserialize, Error, Result};
+use crate::simple::{SValKind, SimpleValue};
+use crate::{Deserialize, Error, Result, Value};
impl<'a, T> crate::sealed::Sealed for T where T: serde::Deserialize<'a> {}
@@ -17,7 +17,7 @@ impl<'a, T> Deserialize for T
where
T: serde::Deserialize<'a>,
{
- fn from_dhall(v: &dhall::Value) -> Result<Self> {
+ fn from_dhall(v: &Value) -> Result<Self> {
let sval = v.to_simple_value().ok_or_else(|| {
Error::Deserialize(format!(
"this cannot be deserialized into the serde data model: {}",
diff --git a/serde_dhall/src/simple.rs b/serde_dhall/src/simple.rs
index 93364a0..0b322ae 100644
--- a/serde_dhall/src/simple.rs
+++ b/serde_dhall/src/simple.rs
@@ -1,25 +1,62 @@
-use super::Error;
-use dhall::{STyKind, SimpleType, SimpleValue};
+use std::collections::BTreeMap;
+
+use dhall::semantics::{Hir, HirKind, Nir, NirKind};
+use dhall::syntax::{Builtin, ExprKind, NumKind, Span};
+
+use crate::Error;
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct SimpleValue {
+ kind: Box<SValKind>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum SValKind {
+ Num(NumKind),
+ Text(String),
+ Optional(Option<SimpleValue>),
+ List(Vec<SimpleValue>),
+ Record(BTreeMap<String, SimpleValue>),
+ Union(String, Option<SimpleValue>),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct SimpleType {
+ kind: Box<STyKind>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum STyKind {
+ Bool,
+ Natural,
+ Integer,
+ Double,
+ Text,
+ Optional(SimpleType),
+ List(SimpleType),
+ Record(BTreeMap<String, SimpleType>),
+ Union(BTreeMap<String, Option<SimpleType>>),
+}
/// A Dhall value. This is a wrapper around [`dhall::SimpleValue`].
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Value(SimpleValue);
+/// A Dhall type. This is a wrapper around [`dhall::SimpleType`].
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Type(SimpleType);
+
impl Value {
pub fn into_simple_value(self) -> SimpleValue {
self.0
}
}
-/// A Dhall type. This is a wrapper around [`dhall::SimpleType`].
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct Type(SimpleType);
-
impl Type {
pub fn into_simple_type(self) -> SimpleType {
self.0
}
- pub fn to_dhall_value(&self) -> dhall::Value {
+ pub fn to_value(&self) -> crate::Value {
self.0.to_value()
}
@@ -52,10 +89,135 @@ impl Type {
}
}
+impl SimpleValue {
+ pub fn new(kind: SValKind) -> Self {
+ SimpleValue {
+ kind: Box::new(kind),
+ }
+ }
+ pub fn from_nir(nir: &Nir) -> Option<Self> {
+ Some(SimpleValue::new(match nir.kind() {
+ NirKind::Num(lit) => SValKind::Num(lit.clone()),
+ NirKind::TextLit(x) => SValKind::Text(
+ x.as_text()
+ .expect("Normal form should ensure the text is a string"),
+ ),
+ NirKind::EmptyOptionalLit(_) => SValKind::Optional(None),
+ NirKind::NEOptionalLit(x) => {
+ SValKind::Optional(Some(Self::from_nir(x)?))
+ }
+ NirKind::EmptyListLit(_) => SValKind::List(vec![]),
+ NirKind::NEListLit(xs) => SValKind::List(
+ xs.iter()
+ .map(|v| Self::from_nir(v))
+ .collect::<Option<_>>()?,
+ ),
+ NirKind::RecordLit(kvs) => SValKind::Record(
+ kvs.iter()
+ .map(|(k, v)| Some((k.into(), Self::from_nir(v)?)))
+ .collect::<Option<_>>()?,
+ ),
+ NirKind::UnionLit(field, x, _) => {
+ SValKind::Union(field.into(), Some(Self::from_nir(x)?))
+ }
+ NirKind::UnionConstructor(field, ty)
+ if ty.get(field).map(|f| f.is_some()) == Some(false) =>
+ {
+ SValKind::Union(field.into(), None)
+ }
+ _ => return None,
+ }))
+ }
+
+ pub fn kind(&self) -> &SValKind {
+ self.kind.as_ref()
+ }
+}
+
+impl SimpleType {
+ pub fn new(kind: STyKind) -> Self {
+ SimpleType {
+ kind: Box::new(kind),
+ }
+ }
+ pub fn from_nir(nir: &Nir) -> Option<Self> {
+ Some(SimpleType::new(match nir.kind() {
+ NirKind::BuiltinType(b) => match b {
+ Builtin::Bool => STyKind::Bool,
+ Builtin::Natural => STyKind::Natural,
+ Builtin::Integer => STyKind::Integer,
+ Builtin::Double => STyKind::Double,
+ Builtin::Text => STyKind::Text,
+ _ => unreachable!(),
+ },
+ NirKind::OptionalType(t) => STyKind::Optional(Self::from_nir(t)?),
+ NirKind::ListType(t) => STyKind::List(Self::from_nir(t)?),
+ NirKind::RecordType(kts) => STyKind::Record(
+ kts.iter()
+ .map(|(k, v)| Some((k.into(), Self::from_nir(v)?)))
+ .collect::<Option<_>>()?,
+ ),
+ NirKind::UnionType(kts) => STyKind::Union(
+ kts.iter()
+ .map(|(k, v)| {
+ Some((
+ k.into(),
+ v.as_ref()
+ .map(|v| Ok(Self::from_nir(v)?))
+ .transpose()?,
+ ))
+ })
+ .collect::<Option<_>>()?,
+ ),
+ _ => return None,
+ }))
+ }
+
+ pub fn kind(&self) -> &STyKind {
+ self.kind.as_ref()
+ }
+ pub fn to_value(&self) -> crate::Value {
+ crate::Value {
+ hir: self.to_hir(),
+ as_simple_val: None,
+ as_simple_ty: Some(self.clone()),
+ }
+ }
+ pub fn to_hir(&self) -> Hir {
+ let hir = |k| Hir::new(HirKind::Expr(k), Span::Artificial);
+ hir(match self.kind() {
+ STyKind::Bool => ExprKind::Builtin(Builtin::Bool),
+ STyKind::Natural => ExprKind::Builtin(Builtin::Natural),
+ STyKind::Integer => ExprKind::Builtin(Builtin::Integer),
+ STyKind::Double => ExprKind::Builtin(Builtin::Double),
+ STyKind::Text => ExprKind::Builtin(Builtin::Text),
+ STyKind::Optional(t) => ExprKind::App(
+ hir(ExprKind::Builtin(Builtin::Optional)),
+ t.to_hir(),
+ ),
+ STyKind::List(t) => {
+ ExprKind::App(hir(ExprKind::Builtin(Builtin::List)), t.to_hir())
+ }
+ STyKind::Record(kts) => ExprKind::RecordType(
+ kts.into_iter()
+ .map(|(k, t)| (k.as_str().into(), t.to_hir()))
+ .collect(),
+ ),
+ STyKind::Union(kts) => ExprKind::UnionType(
+ kts.into_iter()
+ .map(|(k, t)| {
+ (k.as_str().into(), t.as_ref().map(|t| t.to_hir()))
+ })
+ .collect(),
+ ),
+ })
+ }
+}
+
impl super::sealed::Sealed for Value {}
impl super::Deserialize for Value {
- fn from_dhall(v: &dhall::Value) -> super::Result<Self> {
+ fn from_dhall(v: &crate::Value) -> super::Result<Self> {
let sval = v.to_simple_value().ok_or_else(|| {
Error::Deserialize(format!(
"this cannot be deserialized into a simple type: {}",
@@ -69,7 +231,7 @@ impl super::Deserialize for Value {
impl super::sealed::Sealed for Type {}
impl super::Deserialize for Type {
- fn from_dhall(v: &dhall::Value) -> super::Result<Self> {
+ fn from_dhall(v: &crate::Value) -> super::Result<Self> {
let sty = v.to_simple_type().ok_or_else(|| {
Error::Deserialize(format!(
"this cannot be deserialized into a simple type: {}",
diff --git a/serde_dhall/src/static_type.rs b/serde_dhall/src/static_type.rs
index bc5f366..4e30f34 100644
--- a/serde_dhall/src/static_type.rs
+++ b/serde_dhall/src/static_type.rs
@@ -1,5 +1,4 @@
-use dhall::{STyKind, SimpleType};
-
+use crate::simple::{STyKind, SimpleType};
use crate::Type;
/// A Rust type that can be represented as a Dhall type.
diff --git a/serde_dhall/src/value.rs b/serde_dhall/src/value.rs
new file mode 100644
index 0000000..a24f211
--- /dev/null
+++ b/serde_dhall/src/value.rs
@@ -0,0 +1,73 @@
+use dhall::semantics::Hir;
+use dhall::syntax::Expr;
+use dhall::{Normalized, Parsed};
+
+use crate::simple::{SimpleType, SimpleValue};
+use crate::Error;
+
+/// A Dhall value.
+#[derive(Debug, Clone)]
+pub struct Value {
+ /// Invariant: in normal form
+ pub(crate) hir: Hir,
+ /// Cached conversions because they are annoying to construct from Hir.
+ /// At most one of them will be `Some`.
+ pub(crate) as_simple_val: Option<SimpleValue>,
+ pub(crate) as_simple_ty: Option<SimpleType>,
+}
+
+impl Value {
+ /// Parse a string into a Value, and optionally ensure that the value matches the provided type
+ /// annotation.
+ pub fn from_str_with_annot(
+ s: &str,
+ ty: Option<&Self>,
+ ) -> Result<Self, Error> {
+ Self::from_str_with_annot_dhall_error(s, ty).map_err(Error::Dhall)
+ }
+ fn from_normalized(x: &Normalized) -> Self {
+ Value {
+ hir: x.to_hir(),
+ as_simple_val: SimpleValue::from_nir(x.as_nir()),
+ as_simple_ty: SimpleType::from_nir(x.as_nir()),
+ }
+ }
+
+ fn from_str_with_annot_dhall_error(
+ s: &str,
+ ty: Option<&Self>,
+ ) -> Result<Self, dhall::error::Error> {
+ let resolved = Parsed::parse_str(s)?.resolve()?;
+ let typed = match ty {
+ None => resolved.typecheck()?,
+ Some(ty) => resolved.typecheck_with(&ty.hir)?,
+ };
+ Ok(Self::from_normalized(&typed.normalize()))
+ }
+
+ /// Converts a Value into a SimpleValue.
+ pub fn to_simple_value(&self) -> Option<SimpleValue> {
+ self.as_simple_val.clone()
+ }
+ /// Converts a Value into a SimpleType.
+ pub fn to_simple_type(&self) -> Option<SimpleType> {
+ self.as_simple_ty.clone()
+ }
+
+ /// Converts a value back to the corresponding AST expression.
+ pub fn to_expr(&self) -> Expr {
+ self.hir.to_expr(Default::default())
+ }
+}
+
+impl Eq for Value {}
+impl PartialEq for Value {
+ fn eq(&self, other: &Self) -> bool {
+ self.hir == other.hir
+ }
+}
+impl std::fmt::Display for Value {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ self.to_expr().fmt(f)
+ }
+}