#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Span {
pub start: usize,
pub end: usize,
pub line: usize,
pub column: usize,
}
impl Span {
pub fn new(start: usize, end: usize, line: usize, column: usize) -> Self {
Self {
start,
end,
line,
column,
}
}
pub fn merge(&self, other: &Span) -> Span {
Span {
start: self.start.min(other.start),
end: self.end.max(other.end),
line: self.line.min(other.line),
column: if self.line <= other.line {
self.column
} else {
other.column
},
}
}
pub fn len(&self) -> usize {
self.end - self.start
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Visibility {
#[default]
Private,
Public,
PubSpirit,
PubParent,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Version {
pub major: u32,
pub minor: u32,
pub patch: u32,
pub suffix: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ModuleDecl {
pub path: Vec<String>,
pub version: Option<Version>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct UseDecl {
pub visibility: Visibility,
pub source: ImportSource,
pub path: Vec<String>,
pub items: UseItems,
pub alias: Option<String>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum UseItems {
All,
Named(Vec<UseItem>),
Single,
}
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ImportSource {
#[default]
Local,
Registry {
org: String,
package: String,
version: Option<String>,
},
Git {
url: String,
reference: Option<String>,
},
Https {
url: String,
sha256: Option<String>,
},
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct UseItem {
pub name: String,
pub alias: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DolFile {
pub module: Option<ModuleDecl>,
pub uses: Vec<UseDecl>,
pub declarations: Vec<Declaration>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Purity {
#[default]
Pure,
Sex,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Mutability {
#[default]
Immutable,
Mutable,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Declaration {
Gene(Gen),
Trait(Trait),
Constraint(Rule),
System(System),
Evolution(Evo),
Function(Box<FunctionDecl>),
Const(ConstDecl),
SexVar(VarDecl),
}
impl Declaration {
pub fn name(&self) -> &str {
match self {
Declaration::Gene(g) => &g.name,
Declaration::Trait(t) => &t.name,
Declaration::Constraint(c) => &c.name,
Declaration::System(s) => &s.name,
Declaration::Evolution(e) => &e.name,
Declaration::Function(f) => &f.name,
Declaration::Const(c) => &c.name,
Declaration::SexVar(v) => &v.name,
}
}
pub fn exegesis(&self) -> &str {
match self {
Declaration::Gene(g) => &g.exegesis,
Declaration::Trait(t) => &t.exegesis,
Declaration::Constraint(c) => &c.exegesis,
Declaration::System(s) => &s.exegesis,
Declaration::Evolution(e) => &e.exegesis,
Declaration::Function(f) => &f.exegesis,
Declaration::Const(_) | Declaration::SexVar(_) => "", }
}
pub fn span(&self) -> Span {
match self {
Declaration::Gene(g) => g.span,
Declaration::Trait(t) => t.span,
Declaration::Constraint(c) => c.span,
Declaration::System(s) => s.span,
Declaration::Evolution(e) => e.span,
Declaration::Function(f) => f.span,
Declaration::Const(c) => c.span,
Declaration::SexVar(v) => v.span,
}
}
pub fn visibility(&self) -> Visibility {
match self {
Declaration::Gene(g) => g.visibility,
Declaration::Trait(t) => t.visibility,
Declaration::Constraint(c) => c.visibility,
Declaration::System(s) => s.visibility,
Declaration::Function(f) => f.visibility,
Declaration::Const(c) => c.visibility,
Declaration::Evolution(_) | Declaration::SexVar(_) => Visibility::Private,
}
}
pub fn collect_identifiers(&self) -> Vec<String> {
let mut ids = vec![self.name().to_string()];
let statements = match self {
Declaration::Gene(g) => &g.statements,
Declaration::Trait(t) => &t.statements,
Declaration::Constraint(c) => &c.statements,
Declaration::System(s) => &s.statements,
Declaration::Evolution(_)
| Declaration::Function(_)
| Declaration::Const(_)
| Declaration::SexVar(_) => return ids,
};
for stmt in statements {
match stmt {
Statement::Has {
subject, property, ..
} => {
ids.push(subject.clone());
ids.push(property.clone());
}
Statement::HasField(field) => {
ids.push(field.name.clone());
}
Statement::Is { subject, state, .. } => {
ids.push(subject.clone());
ids.push(state.clone());
}
Statement::Uses { reference, .. } => {
ids.push(reference.clone());
}
_ => {}
}
}
ids
}
pub fn collect_dependencies(&self) -> Vec<String> {
let statements = match self {
Declaration::Trait(t) => &t.statements,
Declaration::System(s) => &s.statements,
_ => return vec![],
};
statements
.iter()
.filter_map(|stmt| {
if let Statement::Uses { reference, .. } = stmt {
Some(reference.clone())
} else {
None
}
})
.collect()
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Gen {
pub visibility: Visibility,
pub name: String,
pub extends: Option<String>,
pub statements: Vec<Statement>,
pub exegesis: String,
pub span: Span,
}
#[deprecated(since = "0.8.0", note = "Use `Gen` instead")]
pub type Gene = Gen;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Trait {
pub visibility: Visibility,
pub name: String,
pub statements: Vec<Statement>,
pub exegesis: String,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Rule {
pub visibility: Visibility,
pub name: String,
pub statements: Vec<Statement>,
pub exegesis: String,
pub span: Span,
}
#[deprecated(since = "0.8.0", note = "Use `Rule` instead")]
pub type Constraint = Rule;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct System {
pub visibility: Visibility,
pub name: String,
pub version: String,
pub requirements: Vec<Requirement>,
pub statements: Vec<Statement>,
pub exegesis: String,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Requirement {
pub name: String,
pub constraint: String,
pub version: String,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Evo {
pub name: String,
pub version: String,
pub parent_version: String,
pub additions: Vec<Statement>,
pub deprecations: Vec<Statement>,
pub removals: Vec<String>,
pub rationale: Option<String>,
pub exegesis: String,
pub span: Span,
}
#[deprecated(since = "0.8.0", note = "Use `Evo` instead")]
pub type Evolution = Evo;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Statement {
Has {
subject: String,
property: String,
span: Span,
},
HasField(Box<HasField>),
Is {
subject: String,
state: String,
span: Span,
},
DerivesFrom {
subject: String,
origin: String,
span: Span,
},
Requires {
subject: String,
requirement: String,
span: Span,
},
Uses {
reference: String,
span: Span,
},
Emits {
action: String,
event: String,
span: Span,
},
Matches {
subject: String,
target: String,
span: Span,
},
Never {
subject: String,
action: String,
span: Span,
},
Quantified {
quantifier: Quantifier,
phrase: String,
span: Span,
},
Function(Box<FunctionDecl>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Quantifier {
Each,
All,
}
impl std::fmt::Display for Quantifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Quantifier::Each => write!(f, "each"),
Quantifier::All => write!(f, "all"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Type {
I8,
I16,
I32,
I64,
I128,
U8,
U16,
U32,
U64,
U128,
F32,
F64,
Bool,
String,
Unit,
Vec(Box<Type>),
Option(Box<Type>),
Result(Box<Type>, Box<Type>),
Map(Box<Type>, Box<Type>),
Tuple(Vec<Type>),
Function {
params: Vec<Type>,
ret: Box<Type>,
},
Named(String),
Generic {
name: String,
params: Vec<Type>,
},
Var(usize),
Unknown,
Error,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum BinaryOp {
Add,
Sub,
Mul,
Div,
Mod,
Pow,
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
And,
Or,
Pipe,
Compose,
Apply,
Bind,
Member,
Map,
Ap,
Implies,
Range,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum UnaryOp {
Neg,
Not,
Quote,
Reflect,
Deref,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TypeExpr {
Named(String),
Generic {
name: String,
args: Vec<TypeExpr>,
},
Function {
params: Vec<TypeExpr>,
return_type: Box<TypeExpr>,
},
Tuple(Vec<TypeExpr>),
Never,
Enum {
variants: Vec<EnumVariant>,
},
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct EnumVariant {
pub name: String,
pub fields: Vec<(String, TypeExpr)>,
pub tuple_types: Vec<TypeExpr>,
pub discriminant: Option<i64>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TypeParam {
pub name: String,
pub bounds: Vec<TypeExpr>,
pub default: Option<TypeExpr>,
pub span: Span,
}
#[derive(Debug, Clone, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TypeParams {
pub params: Vec<TypeParam>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ForallExpr {
pub var: String,
pub type_: TypeExpr,
pub body: Box<Expr>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ExistsExpr {
pub var: String,
pub type_: TypeExpr,
pub body: Box<Expr>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Block {
pub statements: Vec<Stmt>,
pub final_expr: Option<Box<Expr>>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Expr {
Literal(Literal),
Identifier(String),
List(Vec<Expr>),
Tuple(Vec<Expr>),
Binary {
left: Box<Expr>,
op: BinaryOp,
right: Box<Expr>,
},
Unary {
op: UnaryOp,
operand: Box<Expr>,
},
Call {
callee: Box<Expr>,
args: Vec<Expr>,
},
StructLiteral {
type_name: String,
fields: Vec<(String, Expr)>,
},
Member {
object: Box<Expr>,
field: String,
},
Lambda {
params: Vec<(String, Option<TypeExpr>)>,
return_type: Option<TypeExpr>,
body: Box<Expr>,
},
If {
condition: Box<Expr>,
then_branch: Box<Expr>,
else_branch: Option<Box<Expr>>,
},
Match {
scrutinee: Box<Expr>,
arms: Vec<MatchArm>,
},
Block(Block),
Quote(Box<Expr>),
Unquote(Box<Expr>),
QuasiQuote(Box<Expr>),
Eval(Box<Expr>),
Reflect(Box<TypeExpr>),
IdiomBracket {
func: Box<Expr>,
args: Vec<Expr>,
},
Forall(ForallExpr),
Exists(ExistsExpr),
Implies {
left: Box<Expr>,
right: Box<Expr>,
span: Span,
},
SexBlock(Block),
Cast {
expr: Box<Expr>,
target_type: TypeExpr,
},
Try(Box<Expr>),
This,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Literal {
Int(i64),
Float(f64),
String(String),
Char(char),
Bool(bool),
Null,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum QuotedExpr {
Literal(Literal),
Ident(String),
Binary {
op: BinaryOp,
left: Box<QuotedExpr>,
right: Box<QuotedExpr>,
},
Unary {
op: UnaryOp,
operand: Box<QuotedExpr>,
},
Call {
callee: Box<QuotedExpr>,
args: Vec<QuotedExpr>,
},
List(Vec<QuotedExpr>),
Lambda {
params: Vec<String>,
body: Box<QuotedExpr>,
},
If {
condition: Box<QuotedExpr>,
then_branch: Box<QuotedExpr>,
else_branch: Option<Box<QuotedExpr>>,
},
Quote(Box<QuotedExpr>),
Unquote(Box<QuotedExpr>),
}
impl QuotedExpr {
pub fn from_expr(expr: &Expr) -> Self {
match expr {
Expr::Literal(lit) => QuotedExpr::Literal(lit.clone()),
Expr::Identifier(name) => QuotedExpr::Ident(name.clone()),
Expr::Binary { left, op, right } => QuotedExpr::Binary {
op: *op,
left: Box::new(QuotedExpr::from_expr(left)),
right: Box::new(QuotedExpr::from_expr(right)),
},
Expr::Unary { op, operand } => QuotedExpr::Unary {
op: *op,
operand: Box::new(QuotedExpr::from_expr(operand)),
},
Expr::Call { callee, args } => QuotedExpr::Call {
callee: Box::new(QuotedExpr::from_expr(callee)),
args: args.iter().map(QuotedExpr::from_expr).collect(),
},
Expr::Lambda {
params,
body,
return_type: _,
} => QuotedExpr::Lambda {
params: params.iter().map(|(name, _)| name.clone()).collect(),
body: Box::new(QuotedExpr::from_expr(body)),
},
Expr::If {
condition,
then_branch,
else_branch,
} => QuotedExpr::If {
condition: Box::new(QuotedExpr::from_expr(condition)),
then_branch: Box::new(QuotedExpr::from_expr(then_branch)),
else_branch: else_branch
.as_ref()
.map(|e| Box::new(QuotedExpr::from_expr(e))),
},
Expr::Quote(inner) => QuotedExpr::Quote(Box::new(QuotedExpr::from_expr(inner))),
Expr::Unquote(inner) => QuotedExpr::Unquote(Box::new(QuotedExpr::from_expr(inner))),
Expr::QuasiQuote(inner) => {
QuotedExpr::Quote(Box::new(QuotedExpr::from_expr(inner)))
}
Expr::Forall(_) | Expr::Exists(_) | Expr::Implies { .. } | Expr::SexBlock { .. } => {
QuotedExpr::Ident(format!("{:?}", expr))
}
_ => QuotedExpr::Ident(format!("{:?}", expr)),
}
}
pub fn to_expr(&self) -> Expr {
match self {
QuotedExpr::Literal(lit) => Expr::Literal(lit.clone()),
QuotedExpr::Ident(name) => Expr::Identifier(name.clone()),
QuotedExpr::Binary { op, left, right } => Expr::Binary {
left: Box::new(left.to_expr()),
op: *op,
right: Box::new(right.to_expr()),
},
QuotedExpr::Unary { op, operand } => Expr::Unary {
op: *op,
operand: Box::new(operand.to_expr()),
},
QuotedExpr::Call { callee, args } => Expr::Call {
callee: Box::new(callee.to_expr()),
args: args.iter().map(|a| a.to_expr()).collect(),
},
QuotedExpr::List(exprs) => {
Expr::Block(Block {
statements: vec![],
final_expr: Some(Box::new(Expr::Call {
callee: Box::new(Expr::Identifier("list".to_string())),
args: exprs.iter().map(|e| e.to_expr()).collect(),
})),
span: Span::default(),
})
}
QuotedExpr::Lambda { params, body } => Expr::Lambda {
params: params.iter().map(|p| (p.clone(), None)).collect(),
return_type: None,
body: Box::new(body.to_expr()),
},
QuotedExpr::If {
condition,
then_branch,
else_branch,
} => Expr::If {
condition: Box::new(condition.to_expr()),
then_branch: Box::new(then_branch.to_expr()),
else_branch: else_branch.as_ref().map(|e| Box::new(e.to_expr())),
},
QuotedExpr::Quote(inner) => Expr::Quote(Box::new(inner.to_expr())),
QuotedExpr::Unquote(inner) => Expr::Unquote(Box::new(inner.to_expr())),
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MatchArm {
pub pattern: Pattern,
pub guard: Option<Box<Expr>>,
pub body: Box<Expr>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Pattern {
Wildcard,
Identifier(String),
Literal(Literal),
Constructor {
name: String,
fields: Vec<Pattern>,
},
Tuple(Vec<Pattern>),
Or(Vec<Pattern>),
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Stmt {
Let {
name: String,
type_ann: Option<TypeExpr>,
value: Expr,
},
Assign {
target: Expr,
value: Expr,
},
For {
binding: String,
iterable: Expr,
body: Vec<Stmt>,
},
While {
condition: Expr,
body: Vec<Stmt>,
},
Loop {
body: Vec<Stmt>,
},
Break,
Continue,
Return(Option<Expr>),
Expr(Expr),
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FunctionParam {
pub name: String,
pub type_ann: TypeExpr,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct VarDecl {
pub mutability: Mutability,
pub name: String,
pub type_ann: Option<TypeExpr>,
pub value: Option<Expr>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ConstDecl {
pub visibility: Visibility,
pub name: String,
pub type_ann: Option<TypeExpr>,
pub value: Expr,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ExternDecl {
pub abi: Option<String>,
pub name: String,
pub params: Vec<FunctionParam>,
pub return_type: Option<TypeExpr>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FunctionDecl {
pub visibility: Visibility,
pub purity: Purity,
pub name: String,
pub type_params: Option<TypeParams>,
pub params: Vec<FunctionParam>,
pub return_type: Option<TypeExpr>,
pub body: Vec<Stmt>,
pub exegesis: String,
pub span: Span,
pub attributes: Vec<String>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct StateDecl {
pub name: String,
pub type_: TypeExpr,
pub default: Option<Expr>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LawDecl {
pub name: String,
pub params: Vec<FunctionParam>,
pub body: Expr,
pub exegesis: Option<String>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct HasField {
pub name: String,
pub type_: TypeExpr,
pub default: Option<Expr>,
pub constraint: Option<Expr>,
pub span: Span,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_span_creation() {
let span = Span::new(0, 10, 1, 1);
assert_eq!(span.len(), 10);
assert!(!span.is_empty());
}
#[test]
fn test_span_merge() {
let span1 = Span::new(0, 5, 1, 1);
let span2 = Span::new(10, 20, 2, 5);
let merged = span1.merge(&span2);
assert_eq!(merged.start, 0);
assert_eq!(merged.end, 20);
}
#[test]
fn test_declaration_name() {
let gen = Gen {
visibility: Visibility::default(),
name: "container.exists".to_string(),
extends: None,
statements: vec![],
exegesis: "Test".to_string(),
span: Span::default(),
};
let decl = Declaration::Gene(gen);
assert_eq!(decl.name(), "container.exists");
}
#[test]
fn test_collect_dependencies() {
let trait_decl = Trait {
visibility: Visibility::default(),
name: "test.trait".to_string(),
statements: vec![
Statement::Uses {
reference: "dep.one".to_string(),
span: Span::default(),
},
Statement::Uses {
reference: "dep.two".to_string(),
span: Span::default(),
},
Statement::Is {
subject: "test".to_string(),
state: "active".to_string(),
span: Span::default(),
},
],
exegesis: "Test".to_string(),
span: Span::default(),
};
let decl = Declaration::Trait(trait_decl);
let deps = decl.collect_dependencies();
assert_eq!(deps.len(), 2);
assert!(deps.contains(&"dep.one".to_string()));
assert!(deps.contains(&"dep.two".to_string()));
}
}