1use crate::{
2 decl_engine::parsed_id::ParsedDeclId,
3 engine_threading::{
4 DebugWithEngines, DisplayWithEngines, EqWithEngines, HashWithEngines, OrdWithEngines,
5 OrdWithEnginesContext, PartialEqWithEngines, PartialEqWithEnginesContext,
6 },
7 language::{parsed::CodeBlock, *},
8 type_system::TypeBinding,
9 Engines, GenericArgument, TypeId,
10};
11use serde::{Deserialize, Serialize};
12use std::{cmp::Ordering, fmt, hash::Hasher};
13use sway_error::handler::ErrorEmitted;
14use sway_types::{ident::Ident, Span, Spanned};
15
16mod asm;
17mod match_branch;
18mod method_name;
19mod scrutinee;
20pub(crate) use asm::*;
21pub(crate) use match_branch::MatchBranch;
22pub use method_name::MethodName;
23pub use scrutinee::*;
24use sway_ast::intrinsics::Intrinsic;
25
26use super::{FunctionDeclaration, StructDeclaration};
27
28#[derive(Debug, Clone)]
30pub struct Expression {
31 pub kind: ExpressionKind,
32 pub span: Span,
33}
34
35#[derive(Debug, Clone)]
36pub struct FunctionApplicationExpression {
37 pub call_path_binding: TypeBinding<CallPath>,
38 pub resolved_call_path_binding:
39 Option<TypeBinding<ResolvedCallPath<ParsedDeclId<FunctionDeclaration>>>>,
40 pub arguments: Vec<Expression>,
41}
42
43impl EqWithEngines for FunctionApplicationExpression {}
44impl PartialEqWithEngines for FunctionApplicationExpression {
45 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
46 self.call_path_binding.eq(&other.call_path_binding, ctx)
47 && self.arguments.eq(&other.arguments, ctx)
48 }
49}
50
51#[derive(Debug, Clone)]
52pub struct LazyOperatorExpression {
53 pub op: LazyOp,
54 pub lhs: Box<Expression>,
55 pub rhs: Box<Expression>,
56}
57
58impl EqWithEngines for LazyOperatorExpression {}
59impl PartialEqWithEngines for LazyOperatorExpression {
60 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
61 self.op == other.op && self.lhs.eq(&other.lhs, ctx) && self.rhs.eq(&other.rhs, ctx)
62 }
63}
64
65#[derive(Debug, Clone)]
66pub struct TupleIndexExpression {
67 pub prefix: Box<Expression>,
68 pub index: usize,
69 pub index_span: Span,
70}
71
72impl EqWithEngines for TupleIndexExpression {}
73impl PartialEqWithEngines for TupleIndexExpression {
74 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
75 self.prefix.eq(&other.prefix, ctx)
76 && self.index == other.index
77 && self.index_span == other.index_span
78 }
79}
80
81#[derive(Debug, Clone)]
82pub enum ArrayExpression {
83 Explicit {
84 contents: Vec<Expression>,
85 length_span: Option<Span>,
86 },
87 Repeat {
88 value: Box<Expression>,
89 length: Box<Expression>,
90 },
91}
92
93impl EqWithEngines for ArrayExpression {}
94impl PartialEqWithEngines for ArrayExpression {
95 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
96 match (self, other) {
97 (
98 ArrayExpression::Explicit {
99 contents: self_contents,
100 length_span: self_length_span,
101 },
102 ArrayExpression::Explicit {
103 contents: other_contents,
104 length_span: other_length_span,
105 },
106 ) => self_contents.eq(other_contents, ctx) && self_length_span == other_length_span,
107 (
108 ArrayExpression::Repeat {
109 value: self_value,
110 length: self_length,
111 },
112 ArrayExpression::Repeat {
113 value: other_value,
114 length: other_length,
115 },
116 ) => self_value.eq(other_value, ctx) && self_length.eq(other_length, ctx),
117 _ => false,
118 }
119 }
120}
121
122#[derive(Debug, Clone)]
123pub struct StructExpression {
124 pub resolved_call_path_binding:
125 Option<TypeBinding<ResolvedCallPath<ParsedDeclId<StructDeclaration>>>>,
126 pub call_path_binding: TypeBinding<CallPath>,
127 pub fields: Vec<StructExpressionField>,
128}
129
130impl EqWithEngines for StructExpression {}
131impl PartialEqWithEngines for StructExpression {
132 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
133 self.call_path_binding.eq(&other.call_path_binding, ctx)
134 && self.fields.eq(&other.fields, ctx)
135 }
136}
137
138#[derive(Debug, Clone)]
139pub struct IfExpression {
140 pub condition: Box<Expression>,
141 pub then: Box<Expression>,
142 pub r#else: Option<Box<Expression>>,
143}
144
145impl EqWithEngines for IfExpression {}
146impl PartialEqWithEngines for IfExpression {
147 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
148 self.condition.eq(&other.condition, ctx)
149 && self.then.eq(&other.then, ctx)
150 && self.r#else.eq(&other.r#else, ctx)
151 }
152}
153
154#[derive(Debug, Clone)]
155pub struct MatchExpression {
156 pub value: Box<Expression>,
157 pub branches: Vec<MatchBranch>,
158}
159
160impl EqWithEngines for MatchExpression {}
161impl PartialEqWithEngines for MatchExpression {
162 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
163 self.value.eq(&other.value, ctx) && self.branches.eq(&other.branches, ctx)
164 }
165}
166
167#[derive(Debug, Clone)]
168pub struct MethodApplicationExpression {
169 pub method_name_binding: TypeBinding<MethodName>,
170 pub contract_call_params: Vec<StructExpressionField>,
171 pub arguments: Vec<Expression>,
172}
173
174impl EqWithEngines for MethodApplicationExpression {}
175impl PartialEqWithEngines for MethodApplicationExpression {
176 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
177 self.method_name_binding.eq(&other.method_name_binding, ctx)
178 && self
179 .contract_call_params
180 .eq(&other.contract_call_params, ctx)
181 && self.arguments.eq(&other.arguments, ctx)
182 }
183}
184
185#[derive(Debug, Clone)]
186pub struct SubfieldExpression {
187 pub prefix: Box<Expression>,
188 pub field_to_access: Ident,
189}
190
191impl EqWithEngines for SubfieldExpression {}
192impl PartialEqWithEngines for SubfieldExpression {
193 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
194 self.prefix.eq(&other.prefix, ctx) && self.field_to_access == other.field_to_access
195 }
196}
197
198#[derive(Debug, Clone)]
199pub struct AmbiguousSuffix {
200 pub before: Option<TypeBinding<Ident>>,
206 pub suffix: Ident,
210}
211
212impl EqWithEngines for AmbiguousSuffix {}
213impl PartialEqWithEngines for AmbiguousSuffix {
214 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
215 self.before.eq(&other.before, ctx) && self.suffix == other.suffix
216 }
217}
218
219impl Spanned for AmbiguousSuffix {
220 fn span(&self) -> Span {
221 if let Some(before) = &self.before {
222 Span::join(before.span(), &self.suffix.span())
223 } else {
224 self.suffix.span()
225 }
226 }
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct QualifiedPathType {
231 pub ty: GenericArgument,
232 pub as_trait: TypeId,
233 pub as_trait_span: Span,
234}
235
236impl HashWithEngines for QualifiedPathType {
237 fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
238 let QualifiedPathType {
239 ty,
240 as_trait,
241 as_trait_span: _,
243 } = self;
244 ty.hash(state, engines);
245 engines.te().get(*as_trait).hash(state, engines);
246 }
247}
248
249impl EqWithEngines for QualifiedPathType {}
250impl PartialEqWithEngines for QualifiedPathType {
251 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
252 let QualifiedPathType {
253 ty,
254 as_trait,
255 as_trait_span: _,
257 } = self;
258 ty.eq(&other.ty, ctx)
259 && ctx
260 .engines()
261 .te()
262 .get(*as_trait)
263 .eq(&ctx.engines().te().get(other.as_trait), ctx)
264 }
265}
266
267impl OrdWithEngines for QualifiedPathType {
268 fn cmp(&self, other: &Self, ctx: &OrdWithEnginesContext) -> Ordering {
269 let QualifiedPathType {
270 ty: l_ty,
271 as_trait: l_as_trait,
272 as_trait_span: _,
274 } = self;
275 let QualifiedPathType {
276 ty: r_ty,
277 as_trait: r_as_trait,
278 as_trait_span: _,
280 } = other;
281 l_ty.cmp(r_ty, ctx).then_with(|| {
282 ctx.engines()
283 .te()
284 .get(*l_as_trait)
285 .cmp(&ctx.engines().te().get(*r_as_trait), ctx)
286 })
287 }
288}
289
290impl DisplayWithEngines for QualifiedPathType {
291 fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
292 write!(
293 f,
294 "<{} as {}>",
295 engines.help_out(self.ty.clone()),
296 engines.help_out(self.as_trait)
297 )
298 }
299}
300
301impl DebugWithEngines for QualifiedPathType {
302 fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
303 write!(f, "{}", engines.help_out(self),)
304 }
305}
306
307#[derive(Debug, Clone)]
308pub struct AmbiguousPathExpression {
309 pub qualified_path_root: Option<QualifiedPathType>,
310 pub call_path_binding: TypeBinding<CallPath<AmbiguousSuffix>>,
311 pub args: Vec<Expression>,
312}
313
314impl EqWithEngines for AmbiguousPathExpression {}
315impl PartialEqWithEngines for AmbiguousPathExpression {
316 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
317 self.qualified_path_root.eq(&other.qualified_path_root, ctx)
318 && PartialEqWithEngines::eq(&self.call_path_binding, &other.call_path_binding, ctx)
319 && self.args.eq(&other.args, ctx)
320 }
321}
322
323#[derive(Debug, Clone)]
324pub struct DelineatedPathExpression {
325 pub call_path_binding: TypeBinding<QualifiedCallPath>,
326 pub args: Option<Vec<Expression>>,
330}
331
332impl EqWithEngines for DelineatedPathExpression {}
333impl PartialEqWithEngines for DelineatedPathExpression {
334 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
335 self.call_path_binding.eq(&other.call_path_binding, ctx) && self.args.eq(&other.args, ctx)
336 }
337}
338
339#[derive(Debug, Clone)]
340pub struct AbiCastExpression {
341 pub abi_name: CallPath,
342 pub address: Box<Expression>,
343}
344
345impl EqWithEngines for AbiCastExpression {}
346impl PartialEqWithEngines for AbiCastExpression {
347 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
348 PartialEqWithEngines::eq(&self.abi_name, &other.abi_name, ctx)
349 && self.address.eq(&other.address, ctx)
350 }
351}
352
353#[derive(Debug, Clone)]
354pub struct ArrayIndexExpression {
355 pub prefix: Box<Expression>,
356 pub index: Box<Expression>,
357}
358
359impl EqWithEngines for ArrayIndexExpression {}
360impl PartialEqWithEngines for ArrayIndexExpression {
361 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
362 self.prefix.eq(&other.prefix, ctx) && self.index.eq(&other.index, ctx)
363 }
364}
365
366#[derive(Debug, Clone)]
367pub struct StorageAccessExpression {
368 pub namespace_names: Vec<Ident>,
369 pub field_names: Vec<Ident>,
370 pub storage_keyword_span: Span,
371}
372
373impl EqWithEngines for StorageAccessExpression {}
374impl PartialEqWithEngines for StorageAccessExpression {
375 fn eq(&self, other: &Self, _ctx: &PartialEqWithEnginesContext) -> bool {
376 self.field_names.eq(&other.field_names)
377 && self.storage_keyword_span.eq(&other.storage_keyword_span)
378 }
379}
380
381#[derive(Debug, Clone)]
382pub struct IntrinsicFunctionExpression {
383 pub name: Ident,
384 pub kind_binding: TypeBinding<Intrinsic>,
385 pub arguments: Vec<Expression>,
386}
387
388impl EqWithEngines for IntrinsicFunctionExpression {}
389impl PartialEqWithEngines for IntrinsicFunctionExpression {
390 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
391 self.name.eq(&other.name)
392 && self.kind_binding.eq(&other.kind_binding, ctx)
393 && self.arguments.eq(&other.arguments, ctx)
394 }
395}
396
397#[derive(Debug, Clone)]
398pub struct WhileLoopExpression {
399 pub condition: Box<Expression>,
400 pub body: CodeBlock,
401 pub is_desugared_for_loop: bool,
402}
403
404impl EqWithEngines for WhileLoopExpression {}
405impl PartialEqWithEngines for WhileLoopExpression {
406 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
407 self.condition.eq(&other.condition, ctx) && self.body.eq(&other.body, ctx)
408 }
409}
410
411#[derive(Debug, Clone)]
412pub struct ForLoopExpression {
413 pub desugared: Box<Expression>,
414}
415
416impl EqWithEngines for ForLoopExpression {}
417impl PartialEqWithEngines for ForLoopExpression {
418 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
419 self.desugared.eq(&other.desugared, ctx)
420 }
421}
422
423#[derive(Debug, Clone)]
424pub struct ReassignmentExpression {
425 pub lhs: ReassignmentTarget,
426 pub rhs: Box<Expression>,
427}
428
429impl EqWithEngines for ReassignmentExpression {}
430impl PartialEqWithEngines for ReassignmentExpression {
431 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
432 self.lhs.eq(&other.lhs, ctx) && self.rhs.eq(&other.rhs, ctx)
433 }
434}
435
436#[derive(Debug, Clone)]
437pub enum ExpressionKind {
438 Error(Box<[Span]>, ErrorEmitted),
444 Literal(Literal),
445 AmbiguousPathExpression(Box<AmbiguousPathExpression>),
448 FunctionApplication(Box<FunctionApplicationExpression>),
449 LazyOperator(LazyOperatorExpression),
450 AmbiguousVariableExpression(Ident),
452 Variable(Ident),
453 Tuple(Vec<Expression>),
454 TupleIndex(TupleIndexExpression),
455 Array(ArrayExpression),
456 Struct(Box<StructExpression>),
457 CodeBlock(CodeBlock),
458 If(IfExpression),
459 Match(MatchExpression),
460 Asm(Box<AsmExpression>),
462 MethodApplication(Box<MethodApplicationExpression>),
463 Subfield(SubfieldExpression),
469 DelineatedPath(Box<DelineatedPathExpression>),
491 AbiCast(Box<AbiCastExpression>),
493 ArrayIndex(ArrayIndexExpression),
494 StorageAccess(StorageAccessExpression),
495 IntrinsicFunction(IntrinsicFunctionExpression),
496 WhileLoop(WhileLoopExpression),
499 ForLoop(ForLoopExpression),
501 Break,
502 Continue,
503 Reassignment(ReassignmentExpression),
504 ImplicitReturn(Box<Expression>),
510 Return(Box<Expression>),
511 Ref(RefExpression),
512 Deref(Box<Expression>),
513}
514
515impl EqWithEngines for Expression {}
516impl PartialEqWithEngines for Expression {
517 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
518 self.kind.eq(&other.kind, ctx)
519 }
520}
521
522impl EqWithEngines for ExpressionKind {}
523impl PartialEqWithEngines for ExpressionKind {
524 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
525 match (self, other) {
526 (ExpressionKind::Error(l_span, _), ExpressionKind::Error(r_span, _)) => {
527 l_span == r_span
528 }
529 (ExpressionKind::Literal(l_literal), ExpressionKind::Literal(r_literal)) => {
530 l_literal == r_literal
531 }
532 (
533 ExpressionKind::AmbiguousPathExpression(lhs),
534 ExpressionKind::AmbiguousPathExpression(rhs),
535 ) => lhs.eq(rhs, ctx),
536 (
537 ExpressionKind::FunctionApplication(lhs),
538 ExpressionKind::FunctionApplication(rhs),
539 ) => lhs.eq(rhs, ctx),
540 (ExpressionKind::LazyOperator(lhs), ExpressionKind::LazyOperator(rhs)) => {
541 lhs.eq(rhs, ctx)
542 }
543 (
544 ExpressionKind::AmbiguousVariableExpression(lhs),
545 ExpressionKind::AmbiguousVariableExpression(rhs),
546 ) => lhs == rhs,
547 (ExpressionKind::Variable(lhs), ExpressionKind::Variable(rhs)) => lhs == rhs,
548 (ExpressionKind::Tuple(lhs), ExpressionKind::Tuple(rhs)) => lhs.eq(rhs, ctx),
549 (ExpressionKind::TupleIndex(lhs), ExpressionKind::TupleIndex(rhs)) => lhs.eq(rhs, ctx),
550 (ExpressionKind::Array(lhs), ExpressionKind::Array(rhs)) => lhs.eq(rhs, ctx),
551 (ExpressionKind::Struct(lhs), ExpressionKind::Struct(rhs)) => lhs.eq(rhs, ctx),
552 (ExpressionKind::CodeBlock(lhs), ExpressionKind::CodeBlock(rhs)) => lhs.eq(rhs, ctx),
553 (ExpressionKind::If(lhs), ExpressionKind::If(rhs)) => lhs.eq(rhs, ctx),
554 (ExpressionKind::Match(lhs), ExpressionKind::Match(rhs)) => lhs.eq(rhs, ctx),
555 (ExpressionKind::Asm(lhs), ExpressionKind::Asm(rhs)) => lhs.eq(rhs, ctx),
556 (ExpressionKind::MethodApplication(lhs), ExpressionKind::MethodApplication(rhs)) => {
557 lhs.eq(rhs, ctx)
558 }
559 (ExpressionKind::Subfield(lhs), ExpressionKind::Subfield(rhs)) => lhs.eq(rhs, ctx),
560 (ExpressionKind::DelineatedPath(lhs), ExpressionKind::DelineatedPath(rhs)) => {
561 lhs.eq(rhs, ctx)
562 }
563 (ExpressionKind::AbiCast(lhs), ExpressionKind::AbiCast(rhs)) => lhs.eq(rhs, ctx),
564 (ExpressionKind::ArrayIndex(lhs), ExpressionKind::ArrayIndex(rhs)) => lhs.eq(rhs, ctx),
565 (ExpressionKind::StorageAccess(lhs), ExpressionKind::StorageAccess(rhs)) => {
566 lhs.eq(rhs, ctx)
567 }
568 (ExpressionKind::IntrinsicFunction(lhs), ExpressionKind::IntrinsicFunction(rhs)) => {
569 lhs.eq(rhs, ctx)
570 }
571 (ExpressionKind::WhileLoop(lhs), ExpressionKind::WhileLoop(rhs)) => lhs.eq(rhs, ctx),
572 (ExpressionKind::ForLoop(lhs), ExpressionKind::ForLoop(rhs)) => lhs.eq(rhs, ctx),
573 (ExpressionKind::Break, ExpressionKind::Break) => true,
574 (ExpressionKind::Continue, ExpressionKind::Continue) => true,
575 (ExpressionKind::Reassignment(lhs), ExpressionKind::Reassignment(rhs)) => {
576 lhs.eq(rhs, ctx)
577 }
578 (ExpressionKind::ImplicitReturn(lhs), ExpressionKind::ImplicitReturn(rhs)) => {
579 lhs.eq(rhs, ctx)
580 }
581 (ExpressionKind::Return(lhs), ExpressionKind::Return(rhs)) => lhs.eq(rhs, ctx),
582 (ExpressionKind::Ref(lhs), ExpressionKind::Ref(rhs)) => lhs.eq(rhs, ctx),
583 (ExpressionKind::Deref(lhs), ExpressionKind::Deref(rhs)) => lhs.eq(rhs, ctx),
584 _ => false,
585 }
586 }
587}
588
589#[derive(Debug, Clone)]
590pub struct RefExpression {
591 pub to_mutable_value: bool,
593 pub value: Box<Expression>,
594}
595
596impl EqWithEngines for RefExpression {}
597impl PartialEqWithEngines for RefExpression {
598 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
599 self.to_mutable_value.eq(&other.to_mutable_value) && self.value.eq(&other.value, ctx)
600 }
601}
602
603#[derive(Debug, Clone)]
604pub enum ReassignmentTarget {
605 ElementAccess(Box<Expression>),
611 Deref(Box<Expression>),
617}
618
619impl EqWithEngines for ReassignmentTarget {}
620impl PartialEqWithEngines for ReassignmentTarget {
621 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
622 match (self, other) {
623 (ReassignmentTarget::ElementAccess(lhs), ReassignmentTarget::ElementAccess(rhs)) => {
624 lhs.eq(rhs, ctx)
625 }
626 (ReassignmentTarget::Deref(lhs), ReassignmentTarget::Deref(rhs)) => lhs.eq(rhs, ctx),
627 _ => false,
628 }
629 }
630}
631
632#[derive(Debug, Clone)]
633pub struct StructExpressionField {
634 pub name: Ident,
635 pub value: Expression,
636}
637
638impl EqWithEngines for StructExpressionField {}
639impl PartialEqWithEngines for StructExpressionField {
640 fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
641 self.name == other.name && self.value.eq(&other.value, ctx)
642 }
643}
644
645impl Spanned for Expression {
646 fn span(&self) -> Span {
647 self.span.clone()
648 }
649}
650
651#[derive(Debug)]
652pub(crate) struct Op {
653 pub span: Span,
654 pub op_variant: OpVariant,
655}
656
657impl Op {
658 pub fn to_method_name(&self) -> Ident {
659 Ident::new_with_override(self.op_variant.method_name().to_string(), self.span.clone())
660 }
661}
662
663#[derive(Debug)]
664pub enum OpVariant {
665 Add,
666 Subtract,
667 Divide,
668 Multiply,
669 Modulo,
670 Or,
671 And,
672 Equals,
673 NotEquals,
674 Xor,
675 BinaryOr,
676 BinaryAnd,
677 GreaterThan,
678 LessThan,
679 GreaterThanOrEqualTo,
680 LessThanOrEqualTo,
681}
682
683impl OpVariant {
684 fn method_name(&self) -> &'static str {
692 use OpVariant::*;
693 match self {
694 Add => "add",
695 Subtract => "subtract",
696 Divide => "divide",
697 Multiply => "multiply",
698 Modulo => "modulo",
699 Or => "$or$",
700 And => "$and$",
701 Equals => "eq",
702 NotEquals => "neq",
703 Xor => "xor",
704 BinaryOr => "binary_or",
705 BinaryAnd => "binary_and",
706 GreaterThan => "gt",
707 LessThan => "lt",
708 LessThanOrEqualTo => "le",
709 GreaterThanOrEqualTo => "ge",
710 }
711 }
712}