From 2dc3b6b2dadff7cd0b6c01ed2087b4c3e3272b41 Mon Sep 17 00:00:00 2001 From: Kennedy Tedesco Date: Thu, 28 Sep 2023 16:24:33 -0300 Subject: [PATCH] new: add Display for all nodes (#1) (#56) --- src/tree/definition/attribute.rs | 71 + src/tree/definition/class.rs | 97 ++ src/tree/definition/constant.rs | 92 ++ src/tree/definition/enum.rs | 136 ++ src/tree/definition/function.rs | 293 ++++ src/tree/definition/interface.rs | 111 ++ src/tree/definition/mod.rs | 15 + src/tree/definition/modifier.rs | 14 + src/tree/definition/namespace.rs | 31 + src/tree/definition/property.rs | 94 ++ src/tree/definition/template.rs | 189 ++- src/tree/definition/type.rs | 472 +++++- src/tree/definition/use.rs | 73 + src/tree/expression/argument.rs | 81 + src/tree/expression/array.rs | 146 ++ src/tree/expression/class.rs | 100 ++ src/tree/expression/construct.rs | 49 + src/tree/expression/control_flow.rs | 59 + src/tree/expression/function.rs | 166 ++ src/tree/expression/generic.rs | 46 + src/tree/expression/literal.rs | 102 ++ src/tree/expression/magic_constant.rs | 29 + src/tree/expression/mod.rs | 43 + src/tree/expression/operator.rs | 2027 +++++++++++++++++++++++++ src/tree/identifier.rs | 53 + src/tree/statement/block.rs | 6 + src/tree/statement/control_flow.rs | 325 ++++ src/tree/statement/expression.rs | 6 + src/tree/statement/loop.rs | 536 +++++++ src/tree/statement/mod.rs | 20 + src/tree/statement/return.rs | 54 + src/tree/statement/try.rs | 168 ++ src/tree/token.rs | 12 + src/tree/utils.rs | 14 + src/tree/variable.rs | 27 +- 35 files changed, 5722 insertions(+), 35 deletions(-) diff --git a/src/tree/definition/attribute.rs b/src/tree/definition/attribute.rs index fd1da7e..c4364d8 100644 --- a/src/tree/definition/attribute.rs +++ b/src/tree/definition/attribute.rs @@ -71,3 +71,74 @@ impl Node for AttributeDefinition { "attribute definition".to_string() } } + +impl std::fmt::Display for AttributeGroupDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "#[{}]", self.members) + } +} + +impl std::fmt::Display for AttributeDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.name)?; + if let Some(arguments) = &self.arguments { + write!(f, "{}", arguments)?; + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + use crate::tree::comment::CommentGroup; + use crate::tree::expression::argument::ArgumentExpression; + use crate::tree::expression::literal::Literal::Integer; + use crate::tree::expression::literal::LiteralInteger; + use crate::tree::expression::Expression; + + #[test] + fn test_attribute_group_definition_display() { + let attribute_group_definition = AttributeGroupDefinition { + hash_left_bracket: 0, + members: CommaSeparated { + inner: vec![ + AttributeDefinition { + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + arguments: None, + }, + AttributeDefinition { + name: Identifier { + position: 0, + value: ByteString::from("Bar"), + }, + arguments: Some(ArgumentListExpression { + comments: CommentGroup { comments: vec![] }, + left_parenthesis: 24, + arguments: CommaSeparated { + inner: vec![ArgumentExpression::Value { + comments: CommentGroup { comments: vec![] }, + value: Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("2"), + })), + }], + commas: vec![], + }, + right_parenthesis: 30, + }), + }, + ], + commas: vec![], + }, + right_bracket: 32, + }; + + assert_eq!(attribute_group_definition.to_string(), "#[Foo, Bar(2)]"); + } +} diff --git a/src/tree/definition/class.rs b/src/tree/definition/class.rs index 5e8fc2c..cb7b740 100644 --- a/src/tree/definition/class.rs +++ b/src/tree/definition/class.rs @@ -226,3 +226,100 @@ impl Node for ClassDefinitionMember { } } } + +impl std::fmt::Display for ClassDefinitionExtends { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {}", self.extends, self.parent) + } +} + +impl std::fmt::Display for ClassDefinitionImplements { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {}", self.implements, self.interfaces) + } +} + +impl std::fmt::Display for ClassDefinitionMember { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::Constant(constant) => write!(f, "{}", constant), + Self::Property(property) => write!(f, "{}", property), + Self::Method(method) => write!(f, "{}", method), + } + } +} + +impl std::fmt::Display for ClassDefinitionBody { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{{ /* ... */ }}") + } +} + +impl std::fmt::Display for ClassDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {} {}", self.modifiers, self.class, self.name)?; + + if let Some(templates) = &self.templates { + write!(f, " {}", templates)?; + } + + if let Some(extends) = &self.extends { + write!(f, " {}", extends)?; + } + + if let Some(implements) = &self.implements { + write!(f, " {}", implements)?; + } + + write!(f, " {}", self.body) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + use crate::tree::definition::modifier::ModifierDefinition; + + #[test] + fn test_class_definition_display() { + let class_definition = ClassDefinition { + comments: CommentGroup { comments: vec![] }, + class: Keyword::new(ByteString::from("class"), 0), + attributes: vec![], + modifiers: ModifierGroupDefinition { + position: 0, + modifiers: vec![ModifierDefinition::Abstract(Keyword::new( + ByteString::from("abstract"), + 0, + ))], + }, + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + templates: None, + extends: Some(ClassDefinitionExtends { + extends: Keyword::new(ByteString::from("extends"), 0), + parent: TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Bar"), + }, + templates: None, + }, + }), + implements: None, + body: ClassDefinitionBody { + left_brace: 19, + members: vec![], + right_brace: 20, + }, + }; + + assert_eq!( + class_definition.to_string(), + "abstract class Foo extends Bar { /* ... */ }" + ); + } +} diff --git a/src/tree/definition/constant.rs b/src/tree/definition/constant.rs index a962b37..39ddf45 100644 --- a/src/tree/definition/constant.rs +++ b/src/tree/definition/constant.rs @@ -102,3 +102,95 @@ impl Node for ClassishConstantDefinition { "classish constant definition".to_string() } } + +impl std::fmt::Display for ConstantDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} {} {} = {};", + self.r#const, self.type_definition, self.name, self.value + ) + } +} + +impl std::fmt::Display for ClassishConstantDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} {} {} {} = {};", + self.modifiers, self.r#const, self.type_definition, self.name, self.value + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + use crate::tree::definition::modifier::ModifierDefinition; + use crate::tree::definition::r#type::SignedIntegerTypeDefinition; + use crate::tree::expression::literal::Literal::Integer; + use crate::tree::expression::literal::LiteralInteger; + + #[test] + fn test_constant_definition_display() { + let constant_definition = ConstantDefinition { + comments: CommentGroup { comments: vec![] }, + r#const: Keyword::new(ByteString::from("const"), 0), + type_definition: TypeDefinition::SignedInteger(SignedIntegerTypeDefinition::I64( + Keyword::new(ByteString::from("i64"), 15), + )), + name: Identifier { + position: 0, + value: ByteString::from("FOO"), + }, + equals: 0, + value: Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("1"), + })), + semicolon: 0, + }; + + assert_eq!( + constant_definition.to_string(), + "const i64 FOO = 1;".to_string() + ); + } + + #[test] + fn test_classish_constant_definition_display() { + let classish_constant_definition = ClassishConstantDefinition { + comments: CommentGroup { comments: vec![] }, + attributes: vec![], + modifiers: ModifierGroupDefinition { + position: 0, + modifiers: vec![ModifierDefinition::Private(Keyword::new( + ByteString::from("private"), + 0, + ))], + }, + r#const: Keyword::new(ByteString::from("const"), 0), + type_definition: TypeDefinition::SignedInteger(SignedIntegerTypeDefinition::I64( + Keyword::new(ByteString::from("i64"), 15), + )), + name: Identifier { + position: 0, + value: ByteString::from("FOO"), + }, + equals: 0, + value: Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("1"), + })), + semicolon: 0, + }; + + assert_eq!( + classish_constant_definition.to_string(), + "private const i64 FOO = 1;".to_string() + ); + } +} diff --git a/src/tree/definition/enum.rs b/src/tree/definition/enum.rs index 4472c23..9d285f0 100644 --- a/src/tree/definition/enum.rs +++ b/src/tree/definition/enum.rs @@ -438,3 +438,139 @@ impl Node for BackedEnumCaseDefinition { "backed enum case definition".to_string() } } + +impl std::fmt::Display for EnumDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::Unit(unit) => write!(f, "{}", unit), + Self::Backed(backed) => write!(f, "{}", backed), + } + } +} + +impl std::fmt::Display for UnitEnumDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {} {}", self.r#enum, self.name, self.body) + } +} + +impl std::fmt::Display for UnitEnumBodyDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{{ /* ... */ }}") + } +} + +impl std::fmt::Display for UnitEnumMemberDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::Case(case) => write!(f, "{}", case), + Self::Method(method) => write!(f, "{}", method), + Self::Constant(constant) => write!(f, "{}", constant), + } + } +} + +impl std::fmt::Display for UnitEnumCaseDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.case) + } +} + +impl std::fmt::Display for BackedEnumDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} {}{} {}", + self.r#enum, self.name, self.backed_type, self.body + ) + } +} + +impl std::fmt::Display for BackedEnumTypeDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::String(.., identifier) => write!(f, ": {}", identifier), + Self::Int(.., identifier) => write!(f, ": {}", identifier), + } + } +} + +impl std::fmt::Display for BackedEnumBodyDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{{ /* ... */ }}") + } +} + +impl std::fmt::Display for EnumImplementsDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {}", self.implements, self.interfaces) + } +} + +impl std::fmt::Display for BackedEnumMemberDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::Case(case) => write!(f, "{}", case), + Self::Method(method) => write!(f, "{}", method), + Self::Constant(constant) => write!(f, "{}", constant), + } + } +} + +impl std::fmt::Display for BackedEnumCaseDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {} = {}", self.case, self.name, self.value) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + + #[test] + fn test_enum_definition_display() { + let backed = BackedEnumDefinition { + comments: CommentGroup { comments: vec![] }, + attributes: vec![], + r#enum: Keyword::new(ByteString::from("enum"), 0), + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + backed_type: BackedEnumTypeDefinition::String( + 0, + Identifier { + position: 0, + value: ByteString::from("string"), + }, + ), + implements: None, + body: BackedEnumBodyDefinition { + left_brace: 0, + members: vec![], + right_brace: 0, + }, + }; + + assert_eq!(backed.to_string(), "enum Foo: string { /* ... */ }"); + + let unit = UnitEnumDefinition { + comments: CommentGroup { comments: vec![] }, + attributes: vec![], + r#enum: Keyword::new(ByteString::from("enum"), 0), + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + implements: None, + body: UnitEnumBodyDefinition { + left_brace: 0, + members: vec![], + right_brace: 0, + }, + }; + + assert_eq!(unit.to_string(), "enum Foo { /* ... */ }"); + } +} diff --git a/src/tree/definition/function.rs b/src/tree/definition/function.rs index d371aaf..44b00da 100644 --- a/src/tree/definition/function.rs +++ b/src/tree/definition/function.rs @@ -468,3 +468,296 @@ impl Node for MethodDefinition { "concrete method definition".to_string() } } + +impl std::fmt::Display for FunctionLikeReturnTypeDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, ": {}", self.type_definition) + } +} + +impl std::fmt::Display for FunctionLikeParameterDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.type_definition)?; + + if self.ellipsis.is_some() { + write!(f, "...")?; + } + + write!(f, " {}", self.variable)?; + + if let Some(default) = self.default.as_ref() { + write!(f, " = {}", default)?; + } + + Ok(()) + } +} + +impl std::fmt::Display for FunctionLikeParameterListDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "({})", self.parameters) + } +} + +impl std::fmt::Display for FunctionLikeParameterDefaultValueDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.value) + } +} + +impl std::fmt::Display for FunctionDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {}", self.function, self.name)?; + + if let Some(templates) = &self.templates { + write!(f, "{}", templates)?; + } + + write!(f, "{}{} {}", self.parameters, self.return_type, self.body) + } +} + +impl std::fmt::Display for MethodParameterDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.modifiers)?; + + write!(f, "{} {}", self.type_definition, self.variable,)?; + + if let Some(default_value) = &self.default { + write!(f, " = {}", default_value)?; + } + + Ok(()) + } +} + +impl std::fmt::Display for MethodParameterListDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "({})", self.parameters) + } +} + +impl std::fmt::Display for MethodTypeConstraintDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} {} {}", + self.type_definition, self.r#is, self.type_definition + ) + } +} + +impl std::fmt::Display for MethodTypeConstraintGroupDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {}", self.r#where, self.constraints) + } +} + +impl std::fmt::Display for MethodBodyDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + MethodBodyDefinition::Concrete(block) => write!(f, "{}", block), + MethodBodyDefinition::Abstract(..) => write!(f, ";"), + } + } +} + +impl std::fmt::Display for MethodDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {} {}", self.modifiers, self.function, self.name)?; + + if let Some(templates) = &self.templates { + write!(f, "{}", templates)?; + } + + write!(f, "{}", self.parameters)?; + + if let Some(return_type) = &self.return_type { + write!(f, "{}", return_type)?; + } + + if let Some(constraints) = &self.constraints { + write!(f, "{}", constraints)?; + } + + write!(f, " {}", self.body) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + use crate::tree::definition::modifier::ModifierDefinition; + use crate::tree::definition::r#type::SignedIntegerTypeDefinition; + use crate::tree::definition::template::TemplateDefinition; + use crate::tree::definition::template::TemplateDefinitionTypeConstraint; + use crate::tree::definition::template::TemplateDefinitionVariance; + use crate::tree::identifier::TemplatedIdentifier; + + #[test] + fn test_function_definition_display() { + let function_definition = FunctionDefinition { + function: Keyword::new(ByteString::from("function"), 0), + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + templates: None, + parameters: FunctionLikeParameterListDefinition { + comments: CommentGroup { comments: vec![] }, + left_parenthesis: 0, + parameters: CommaSeparated { + inner: vec![FunctionLikeParameterDefinition { + attributes: vec![], + comments: CommentGroup { comments: vec![] }, + type_definition: TypeDefinition::SignedInteger( + SignedIntegerTypeDefinition::I32(Keyword::new( + ByteString::from("i32"), + 15, + )), + ), + ellipsis: None, + variable: Variable { + position: 0, + name: ByteString::from("foo"), + }, + default: None, + }], + commas: vec![], + }, + right_parenthesis: 0, + }, + return_type: FunctionLikeReturnTypeDefinition { + colon: 0, + type_definition: TypeDefinition::SignedInteger(SignedIntegerTypeDefinition::I64( + Keyword::new(ByteString::from("i64"), 15), + )), + }, + body: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + comments: CommentGroup { comments: vec![] }, + attributes: vec![], + }; + + assert_eq!( + function_definition.to_string(), + "function Foo(i32 $foo): i64 { /* ... */ }" + ); + } + + #[test] + fn test_method_definition_display() { + let method_definition = MethodDefinition { + modifiers: ModifierGroupDefinition { + position: 0, + modifiers: vec![ModifierDefinition::Public(Keyword::new( + ByteString::from("public"), + 0, + ))], + }, + function: Keyword::new(ByteString::from("function"), 0), + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + templates: Some(TemplateGroupDefinition { + comments: CommentGroup { comments: vec![] }, + less_than: 0, + members: CommaSeparated { + inner: vec![ + TemplateDefinition { + variance: TemplateDefinitionVariance::Invaraint, + name: Identifier { + position: 1, + value: ByteString::from("T"), + }, + constraint: TemplateDefinitionTypeConstraint::None, + }, + TemplateDefinition { + variance: TemplateDefinitionVariance::Invaraint, + name: Identifier { + position: 1, + value: ByteString::from("U"), + }, + constraint: TemplateDefinitionTypeConstraint::None, + }, + ], + commas: vec![], + }, + greater_than: 4, + }), + parameters: MethodParameterListDefinition { + comments: CommentGroup { comments: vec![] }, + left_parenthesis: 0, + parameters: CommaSeparated { + inner: vec![ + MethodParameterDefinition { + attributes: vec![], + comments: CommentGroup { comments: vec![] }, + modifiers: ModifierGroupDefinition { + position: 0, + modifiers: vec![], + }, + type_definition: TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("T"), + }, + templates: None, + }), + ellipsis: None, + variable: Variable { + position: 0, + name: ByteString::from("bar"), + }, + default: None, + }, + MethodParameterDefinition { + attributes: vec![], + comments: CommentGroup { comments: vec![] }, + modifiers: ModifierGroupDefinition { + position: 0, + modifiers: vec![], + }, + type_definition: TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("U"), + }, + templates: None, + }), + ellipsis: None, + variable: Variable { + position: 0, + name: ByteString::from("baz"), + }, + default: None, + }, + ], + commas: vec![], + }, + right_parenthesis: 0, + }, + return_type: None, + constraints: None, + body: MethodBodyDefinition::Concrete(BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }), + comments: CommentGroup { comments: vec![] }, + attributes: vec![], + }; + + assert_eq!( + method_definition.to_string(), + "public function Foo(T $bar, U $baz) { /* ... */ }" + ); + } +} diff --git a/src/tree/definition/interface.rs b/src/tree/definition/interface.rs index 04edba0..ab9c8d6 100644 --- a/src/tree/definition/interface.rs +++ b/src/tree/definition/interface.rs @@ -176,3 +176,114 @@ impl Node for InterfaceDefinitionMember { } } } + +impl std::fmt::Display for InterfaceDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {}", self.interface, self.name)?; + + if let Some(templates) = &self.templates { + write!(f, "{}", templates)?; + } + + if let Some(extends) = &self.extends { + write!(f, " {}", extends)?; + } + + write!(f, " {}", self.body) + } +} + +impl std::fmt::Display for InterfaceDefinitionExtends { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {}", self.extends, self.parents) + } +} + +impl std::fmt::Display for InterfaceDefinitionBody { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{{ /* ... */ }}") + } +} + +impl std::fmt::Display for InterfaceDefinitionMember { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::Constant(constant) => write!(f, "{}", constant), + Self::Method(method) => write!(f, "{}", method), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + use crate::tree::definition::r#type::TypeDefinition; + use crate::tree::definition::template::TemplateDefinition; + use crate::tree::definition::template::TemplateDefinitionTypeConstraint; + use crate::tree::definition::template::TemplateDefinitionVariance; + + #[test] + pub fn test_interface_definition_display() { + let interface_definition = InterfaceDefinition { + comments: CommentGroup { comments: vec![] }, + attributes: vec![], + interface: Keyword::new(ByteString::from("interface"), 0), + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + templates: Some(TemplateGroupDefinition { + comments: CommentGroup { comments: vec![] }, + less_than: 0, + members: CommaSeparated { + inner: vec![TemplateDefinition { + variance: TemplateDefinitionVariance::Invaraint, + name: Identifier { + position: 1, + value: ByteString::from("U"), + }, + constraint: TemplateDefinitionTypeConstraint::SubType( + Keyword { + value: ByteString::from("as"), + position: 2, + }, + TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 3, + value: ByteString::from("IFoo"), + }, + templates: None, + }), + ), + }], + commas: vec![], + }, + greater_than: 0, + }), + extends: Some(InterfaceDefinitionExtends { + extends: Keyword::new(ByteString::from("extends"), 0), + parents: CommaSeparated { + inner: vec![TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Bar"), + }, + templates: None, + }], + commas: vec![], + }, + }), + body: InterfaceDefinitionBody { + left_brace: 0, + members: vec![], + right_brace: 0, + }, + }; + + assert_eq!( + interface_definition.to_string(), + "interface Foo extends Bar { /* ... */ }" + ); + } +} diff --git a/src/tree/definition/mod.rs b/src/tree/definition/mod.rs index d55144a..96b9626 100644 --- a/src/tree/definition/mod.rs +++ b/src/tree/definition/mod.rs @@ -118,3 +118,18 @@ impl Node for Definition { } } } + +impl std::fmt::Display for Definition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::Namespace(definition) => write!(f, "{}", definition), + Self::Use(definition) => write!(f, "{}", definition), + Self::TypeAlias(definition) => write!(f, "{}", definition), + Self::Constant(definition) => write!(f, "{}", definition), + Self::Function(definition) => write!(f, "{}", definition), + Self::Interface(definition) => write!(f, "{}", definition), + Self::Enum(definition) => write!(f, "{}", definition), + Self::Class(definition) => write!(f, "{}", definition), + } + } +} diff --git a/src/tree/definition/modifier.rs b/src/tree/definition/modifier.rs index fa84638..5846736 100644 --- a/src/tree/definition/modifier.rs +++ b/src/tree/definition/modifier.rs @@ -122,3 +122,17 @@ impl std::fmt::Display for ModifierDefinition { } } } + +impl std::fmt::Display for ModifierGroupDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(modifier) = self.modifiers.first() { + write!(f, "{}", modifier)?; + } + + for modifier in self.modifiers.iter().skip(1) { + write!(f, " {}", modifier)?; + } + + Ok(()) + } +} diff --git a/src/tree/definition/namespace.rs b/src/tree/definition/namespace.rs index 7b47f1b..ce34226 100644 --- a/src/tree/definition/namespace.rs +++ b/src/tree/definition/namespace.rs @@ -41,3 +41,34 @@ impl Node for NamespaceDefinition { "namespace definition".to_string() } } + +impl std::fmt::Display for NamespaceDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{} {};", self.namespace, self.name)?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + + #[test] + fn test_namespace_definition_display() { + let namespace_definition = NamespaceDefinition { + namespace: Keyword { + value: ByteString::from("namespace"), + position: 0, + }, + name: Identifier { + position: 0, + value: ByteString::from("Foo\\Bar"), + }, + semicolon: 14, + definitions: vec![], + }; + + assert_eq!(namespace_definition.to_string(), "namespace Foo\\Bar;"); + } +} diff --git a/src/tree/definition/property.rs b/src/tree/definition/property.rs index 6d305cd..6fee74e 100644 --- a/src/tree/definition/property.rs +++ b/src/tree/definition/property.rs @@ -110,3 +110,97 @@ impl Node for PropertyEntryDefinition { } } } + +impl std::fmt::Display for PropertyDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} {} {};", + self.modifiers, self.type_definition, self.entry + ) + } +} + +impl std::fmt::Display for PropertyEntryDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::Uninitialized { variable } => write!(f, "{}", variable), + Self::Initialized { + variable, value, .. + } => write!(f, "{} = {}", variable, value), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + use crate::tree::comment::CommentGroup; + use crate::tree::definition::modifier::ModifierDefinition; + use crate::tree::definition::r#type::UnsignedIntegerTypeDefinition; + use crate::tree::expression::literal::Literal::Integer; + use crate::tree::expression::literal::LiteralInteger; + use crate::tree::token::Keyword; + + #[test] + pub fn test_property_definition_display() { + let uninitialized_property_definition = PropertyDefinition { + attributes: vec![], + modifiers: ModifierGroupDefinition { + position: 0, + modifiers: vec![ + ModifierDefinition::Public(Keyword::new(ByteString::from("public"), 0)), + ModifierDefinition::Readonly(Keyword::new(ByteString::from("readonly"), 0)), + ], + }, + type_definition: TypeDefinition::UnsignedInteger(UnsignedIntegerTypeDefinition::U32( + Keyword::new(ByteString::from("u32"), 15), + )), + entry: PropertyEntryDefinition::Uninitialized { + variable: Variable { + position: 0, + name: ByteString::from("foo"), + }, + }, + semicolon: 0, + }; + + assert_eq!( + uninitialized_property_definition.to_string(), + "public readonly u32 $foo;" + ); + + let initialized_property_definition = PropertyDefinition { + attributes: vec![], + modifiers: ModifierGroupDefinition { + position: 0, + modifiers: vec![ + ModifierDefinition::Public(Keyword::new(ByteString::from("public"), 0)), + ModifierDefinition::Readonly(Keyword::new(ByteString::from("readonly"), 0)), + ], + }, + type_definition: TypeDefinition::UnsignedInteger(UnsignedIntegerTypeDefinition::U8( + Keyword::new(ByteString::from("u8"), 7), + )), + entry: PropertyEntryDefinition::Initialized { + variable: Variable { + position: 0, + name: ByteString::from("bar"), + }, + equals: 0, + value: Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("123"), + })), + }, + semicolon: 0, + }; + + assert_eq!( + initialized_property_definition.to_string(), + "public readonly u8 $bar = 123;" + ); + } +} diff --git a/src/tree/definition/template.rs b/src/tree/definition/template.rs index 6f0ef8b..472e2a7 100644 --- a/src/tree/definition/template.rs +++ b/src/tree/definition/template.rs @@ -124,6 +124,21 @@ impl Node for TypeTemplateGroupDefinition { } } +impl std::fmt::Display for TemplateGroupDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "<{}>", + self.members + .inner + .iter() + .map(|type_definition| { type_definition.to_string() }) + .collect::>() + .join(", ") + ) + } +} + impl std::fmt::Display for TypeTemplateGroupDefinition { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( @@ -132,9 +147,181 @@ impl std::fmt::Display for TypeTemplateGroupDefinition { self.members .inner .iter() - .map(|s| { s.to_string() }) + .map(|type_definition| { type_definition.to_string() }) .collect::>() .join(", ") ) } } + +impl std::fmt::Display for TemplateDefinitionVariance { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Covariance(_) => write!(f, "+"), + Self::Invaraint => write!(f, ""), + } + } +} + +impl std::fmt::Display for TemplateDefinitionTypeConstraint { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::SubType(k, t) => write!(f, " {k} {t}"), + Self::None => write!(f, ""), + } + } +} + +impl std::fmt::Display for TemplateDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Ok(write!( + f, + "{}{}{}", + &self.variance, self.name, &self.constraint + )?) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + use crate::tree::identifier::TemplatedIdentifier; + + #[test] + fn test_template_definition_display() { + let template_definition = TemplateDefinition { + variance: TemplateDefinitionVariance::Covariance(0), + name: Identifier { + position: 1, + value: ByteString::from("T"), + }, + constraint: TemplateDefinitionTypeConstraint::SubType( + Keyword { + value: ByteString::from("as"), + position: 2, + }, + TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 3, + value: ByteString::from("object"), + }, + templates: None, + }), + ), + }; + + assert_eq!(template_definition.to_string(), "+T as object"); + + let template_definition = TemplateDefinition { + variance: TemplateDefinitionVariance::Invaraint, + name: Identifier { + position: 1, + value: ByteString::from("U"), + }, + constraint: TemplateDefinitionTypeConstraint::SubType( + Keyword { + value: ByteString::from("as"), + position: 2, + }, + TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 3, + value: ByteString::from("IFoo"), + }, + templates: None, + }), + ), + }; + + assert_eq!(template_definition.to_string(), "U as IFoo"); + } + + #[test] + fn test_template_group_definition_display() { + let template_group_definition = TemplateGroupDefinition { + comments: CommentGroup { comments: vec![] }, + less_than: 0, + members: CommaSeparated { + inner: vec![ + TemplateDefinition { + variance: TemplateDefinitionVariance::Covariance(0), + name: Identifier { + position: 1, + value: ByteString::from("T"), + }, + constraint: TemplateDefinitionTypeConstraint::SubType( + Keyword { + value: ByteString::from("as"), + position: 2, + }, + TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 3, + value: ByteString::from("object"), + }, + templates: None, + }), + ), + }, + TemplateDefinition { + variance: TemplateDefinitionVariance::Invaraint, + name: Identifier { + position: 1, + value: ByteString::from("U"), + }, + constraint: TemplateDefinitionTypeConstraint::SubType( + Keyword { + value: ByteString::from("as"), + position: 2, + }, + TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 3, + value: ByteString::from("IFoo"), + }, + templates: None, + }), + ), + }, + ], + commas: vec![], + }, + greater_than: 4, + }; + + assert_eq!( + template_group_definition.to_string(), + "<+T as object, U as IFoo>" + ); + + let template_group_definition = TemplateGroupDefinition { + comments: CommentGroup { comments: vec![] }, + less_than: 0, + members: CommaSeparated { + inner: vec![ + TemplateDefinition { + variance: TemplateDefinitionVariance::Invaraint, + name: Identifier { + position: 1, + value: ByteString::from("T"), + }, + constraint: TemplateDefinitionTypeConstraint::None, + }, + TemplateDefinition { + variance: TemplateDefinitionVariance::Invaraint, + name: Identifier { + position: 1, + value: ByteString::from("U"), + }, + constraint: TemplateDefinitionTypeConstraint::None, + }, + ], + commas: vec![], + }, + greater_than: 4, + }; + + assert_eq!(template_group_definition.to_string(), ""); + } +} diff --git a/src/tree/definition/type.rs b/src/tree/definition/type.rs index 0ee275d..7a5a036 100644 --- a/src/tree/definition/type.rs +++ b/src/tree/definition/type.rs @@ -353,7 +353,6 @@ impl Node for TypeDefinition { fn get_description(&self) -> String { match &self { - // match &self and print all the variants: Self::Identifier(_) => "identifier type definition".to_string(), Self::Union(_) => "union type definition".to_string(), Self::Intersection(_) => "intersection type definition".to_string(), @@ -381,6 +380,16 @@ impl Node for TypeDefinition { } } +impl std::fmt::Display for TypeAliasDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} {} = {};", + self.r#type, self.name, self.type_definition, + ) + } +} + impl std::fmt::Display for TypeDefinition { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match &self { @@ -407,33 +416,10 @@ impl std::fmt::Display for TypeDefinition { Self::Void(_) => write!(f, "void"), Self::Never(_) => write!(f, "never"), Self::Boolean(_) => write!(f, "bool"), - Self::Literal(literal) => match literal { - Literal::Null(_) => write!(f, "null"), - Literal::False(_) => write!(f, "false"), - Literal::True(_) => write!(f, "true"), - Literal::Integer(inner) => write!(f, "{}", inner.value), - Literal::Float(inner) => write!(f, "{}", inner.value), - Literal::String(inner) => write!(f, "{}", inner.value), - }, - Self::SignedInteger(signed) => match signed { - SignedIntegerTypeDefinition::Default(_) => write!(f, "int"), - SignedIntegerTypeDefinition::I128(_) => write!(f, "i128"), - SignedIntegerTypeDefinition::I64(_) => write!(f, "i64"), - SignedIntegerTypeDefinition::I32(_) => write!(f, "i32"), - SignedIntegerTypeDefinition::I16(_) => write!(f, "i16"), - SignedIntegerTypeDefinition::I8(_) => write!(f, "i8"), - }, - Self::UnsignedInteger(unsigned) => match unsigned { - UnsignedIntegerTypeDefinition::Default(_) => write!(f, "uint"), - UnsignedIntegerTypeDefinition::U32(_) => write!(f, "u32"), - UnsignedIntegerTypeDefinition::U16(_) => write!(f, "u16"), - UnsignedIntegerTypeDefinition::U8(_) => write!(f, "u8"), - }, - Self::FloatingPoint(floating) => match floating { - FloatingPointTypeDefinition::Default(_) => write!(f, "float"), - FloatingPointTypeDefinition::F64(_) => write!(f, "f64"), - FloatingPointTypeDefinition::F32(_) => write!(f, "f32"), - }, + Self::Literal(literal) => write!(f, "{literal}"), + Self::SignedInteger(signed) => write!(f, "{signed}"), + Self::UnsignedInteger(unsigned) => write!(f, "{unsigned}"), + Self::FloatingPoint(floating) => write!(f, "{floating}"), Self::String(_) => write!(f, "string"), Self::Dict(_, template) => write!(f, "dict{template}"), Self::Vec(_, template) => write!(f, "vec{template}"), @@ -464,3 +450,433 @@ impl std::fmt::Display for TypeDefinition { } } } + +impl std::fmt::Display for SignedIntegerTypeDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Default(_) => write!(f, "int"), + Self::I128(_) => write!(f, "i128"), + Self::I64(_) => write!(f, "i64"), + Self::I32(_) => write!(f, "i32"), + Self::I16(_) => write!(f, "i16"), + Self::I8(_) => write!(f, "i8"), + } + } +} + +impl std::fmt::Display for UnsignedIntegerTypeDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Default(_) => write!(f, "uint"), + Self::U32(_) => write!(f, "u32"), + Self::U16(_) => write!(f, "u16"), + Self::U8(_) => write!(f, "u8"), + } + } +} + +impl std::fmt::Display for FloatingPointTypeDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Default(_) => write!(f, "float"), + Self::F64(_) => write!(f, "f64"), + Self::F32(_) => write!(f, "f32"), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + use crate::tree::comment::CommentGroup; + use crate::tree::identifier::Identifier; + + #[test] + fn test_type_alias_definition_display() { + let type_alias_definition = TypeAliasDefinition { + r#type: Keyword { + value: ByteString::from("type"), + position: 0, + }, + name: TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + templates: None, + }, + equals: 0, + type_definition: TypeDefinition::UnsignedInteger(UnsignedIntegerTypeDefinition::U32( + Keyword { + value: ByteString::from("u32"), + position: 0, + }, + )), + semicolon: 0, + }; + + assert_eq!(type_alias_definition.to_string(), "type Foo = u32;"); + } + + #[test] + fn test_type_alias_definition_with_templates_display() { + let type_alias_definition = TypeAliasDefinition { + r#type: Keyword { + value: ByteString::from("type"), + position: 0, + }, + name: TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("filter"), + }, + templates: Some(TypeTemplateGroupDefinition { + comments: CommentGroup { comments: vec![] }, + less_than: 0, + members: CommaSeparated { + inner: vec![TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 3, + value: ByteString::from("T"), + }, + templates: None, + })], + commas: vec![], + }, + greater_than: 0, + }), + }, + equals: 0, + type_definition: TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 3, + value: ByteString::from("Closure"), + }, + templates: Some(TypeTemplateGroupDefinition { + comments: CommentGroup { comments: vec![] }, + less_than: 0, + members: CommaSeparated { + inner: vec![ + TypeDefinition::Tuple { + left_parenthesis: 0, + type_definitions: CommaSeparated { + inner: vec![TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 3, + value: ByteString::from("T"), + }, + templates: None, + })], + commas: vec![], + }, + right_parenthesis: 0, + }, + TypeDefinition::Boolean(Keyword { + value: ByteString::from("bool"), + position: 0, + }), + ], + commas: vec![0], + }, + greater_than: 0, + }), + }), + semicolon: 0, + }; + + assert_eq!( + type_alias_definition.to_string(), + "type filter = Closure<(T), bool>;" + ); + } + + #[test] + fn test_type_definition_display() { + let type_definition = TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + templates: None, + }); + + assert_eq!(type_definition.to_string(), "Foo"); + + let nullable = TypeDefinition::Nullable( + 0, + Box::new(TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + templates: None, + })), + ); + + assert_eq!(nullable.to_string(), "?Foo"); + + let union = TypeDefinition::Union(vec![ + TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + templates: None, + }), + TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Bar"), + }, + templates: None, + }), + ]); + + assert_eq!(union.to_string(), "Foo|Bar"); + + let intersection = TypeDefinition::Intersection(vec![ + TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + templates: None, + }), + TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Bar"), + }, + templates: None, + }), + ]); + + assert_eq!(intersection.to_string(), "Foo&Bar"); + + let void = TypeDefinition::Void(Keyword { + value: ByteString::from("void"), + position: 0, + }); + + assert_eq!(void.to_string(), "void"); + + let never = TypeDefinition::Never(Keyword { + value: ByteString::from("never"), + position: 0, + }); + + assert_eq!(never.to_string(), "never"); + + let boolean = TypeDefinition::Boolean(Keyword { + value: ByteString::from("bool"), + position: 0, + }); + + assert_eq!(boolean.to_string(), "bool"); + + let string = TypeDefinition::String(Keyword { + value: ByteString::from("string"), + position: 0, + }); + + assert_eq!(string.to_string(), "string"); + + let dict = TypeDefinition::Dict( + Keyword { + value: ByteString::from("dict"), + position: 0, + }, + TypeTemplateGroupDefinition { + comments: CommentGroup { comments: vec![] }, + less_than: 0, + members: CommaSeparated { + inner: vec![ + TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + templates: None, + }), + TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Bar"), + }, + templates: None, + }), + ], + commas: vec![0], + }, + greater_than: 0, + }, + ); + + assert_eq!(dict.to_string(), "dict"); + + let vec = TypeDefinition::Vec( + Keyword { + value: ByteString::from("vec"), + position: 0, + }, + TypeTemplateGroupDefinition { + comments: CommentGroup { comments: vec![] }, + less_than: 0, + members: CommaSeparated { + inner: vec![TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + templates: None, + })], + commas: vec![], + }, + greater_than: 0, + }, + ); + + assert_eq!(vec.to_string(), "vec"); + + let object = TypeDefinition::Object(Keyword { + value: ByteString::from("object"), + position: 0, + }); + + assert_eq!(object.to_string(), "object"); + + let mixed = TypeDefinition::Mixed(Keyword { + value: ByteString::from("mixed"), + position: 0, + }); + + assert_eq!(mixed.to_string(), "mixed"); + + let nonnull = TypeDefinition::NonNull(Keyword { + value: ByteString::from("nonnull"), + position: 0, + }); + + assert_eq!(nonnull.to_string(), "nonnull"); + + let resource = TypeDefinition::Resource(Keyword { + value: ByteString::from("resource"), + position: 0, + }); + + assert_eq!(resource.to_string(), "resource"); + + let iterable = TypeDefinition::Iterable( + Keyword { + value: ByteString::from("iterable"), + position: 0, + }, + TypeTemplateGroupDefinition { + comments: CommentGroup { comments: vec![] }, + less_than: 0, + members: CommaSeparated { + inner: vec![TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + templates: None, + })], + commas: vec![], + }, + greater_than: 0, + }, + ); + + assert_eq!(iterable.to_string(), "iterable"); + + let class = TypeDefinition::Class( + Keyword { + value: ByteString::from("class"), + position: 0, + }, + TypeTemplateGroupDefinition { + comments: CommentGroup { comments: vec![] }, + less_than: 0, + members: CommaSeparated { + inner: vec![TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + templates: None, + })], + commas: vec![], + }, + greater_than: 0, + }, + ); + + assert_eq!(class.to_string(), "class"); + + let interface = TypeDefinition::Interface( + Keyword { + value: ByteString::from("interface"), + position: 0, + }, + TypeTemplateGroupDefinition { + comments: CommentGroup { comments: vec![] }, + less_than: 0, + members: CommaSeparated { + inner: vec![TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + templates: None, + })], + commas: vec![], + }, + greater_than: 0, + }, + ); + + assert_eq!(interface.to_string(), "interface"); + + let tuple = TypeDefinition::Tuple { + left_parenthesis: 0, + type_definitions: CommaSeparated { + inner: vec![ + TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + templates: None, + }), + TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Bar"), + }, + templates: None, + }), + ], + commas: vec![0], + }, + right_parenthesis: 0, + }; + + assert_eq!(tuple.to_string(), "(Foo, Bar)"); + + let parenthesized = TypeDefinition::Parenthesized { + left_parenthesis: 0, + type_definition: Box::new(TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + templates: None, + })), + right_parenthesis: 0, + }; + + assert_eq!(parenthesized.to_string(), "(Foo)"); + } +} diff --git a/src/tree/definition/use.rs b/src/tree/definition/use.rs index 84b2d50..fb7698c 100644 --- a/src/tree/definition/use.rs +++ b/src/tree/definition/use.rs @@ -122,3 +122,76 @@ impl Node for UseDefinitionSymbolAlias { "use symbol alias definition".to_string() } } + +impl std::fmt::Display for UseDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::Default { r#use, name, .. } => write!(f, "{} {}", r#use, name), + Self::Function { + r#use, + function: r#type, + name, + .. + } => write!(f, "{} {} {}", r#use, r#type, name), + Self::Constant { + r#use, + r#const: r#type, + name, + .. + } => write!(f, "{} {} {}", r#use, r#type, name), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + + #[test] + fn test_use_definition_display() { + let use_definition = UseDefinition::Default { + r#use: Keyword::new(ByteString::from("use"), 0), + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + alias: None, + semicolon: 0, + }; + + assert_eq!(use_definition.to_string(), "use Foo"); + } + + #[test] + fn test_use_function_definition_display() { + let use_definition = UseDefinition::Function { + r#use: Keyword::new(ByteString::from("use"), 0), + function: Keyword::new(ByteString::from("function"), 0), + name: Identifier { + position: 0, + value: ByteString::from("foo_bar"), + }, + alias: None, + semicolon: 0, + }; + + assert_eq!(use_definition.to_string(), "use function foo_bar"); + } + + #[test] + fn test_use_const_definition_display() { + let use_definition = UseDefinition::Constant { + r#use: Keyword::new(ByteString::from("use"), 0), + r#const: Keyword::new(ByteString::from("const"), 0), + name: Identifier { + position: 0, + value: ByteString::from("FOO"), + }, + alias: None, + semicolon: 0, + }; + + assert_eq!(use_definition.to_string(), "use const FOO"); + } +} diff --git a/src/tree/expression/argument.rs b/src/tree/expression/argument.rs index 5d450ef..15a9f68 100644 --- a/src/tree/expression/argument.rs +++ b/src/tree/expression/argument.rs @@ -147,3 +147,84 @@ impl Node for ArgumentPlaceholderExpression { "argument placeholder expression".to_string() } } + +impl std::fmt::Display for ArgumentExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ArgumentExpression::Value { value, .. } => write!(f, "{}", value), + ArgumentExpression::Spread { value, .. } => write!(f, "...{}", value), + ArgumentExpression::ReverseSpread { value, .. } => write!(f, "{}...", value), + ArgumentExpression::Named { name, value, .. } => write!(f, "{}: {}", name, value), + } + } +} + +impl std::fmt::Display for ArgumentListExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "({})", self.arguments) + } +} + +impl std::fmt::Display for ArgumentPlaceholderExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "(...)") + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + use crate::tree::expression::literal::Literal::Integer; + use crate::tree::expression::literal::LiteralInteger; + use crate::tree::expression::Expression; + use crate::tree::identifier::Identifier; + use crate::tree::variable::Variable; + + #[test] + fn test_argument_expression_display() { + let argument = ArgumentExpression::Value { + comments: CommentGroup { comments: vec![] }, + value: Expression::Variable(Variable { + position: 0, + name: ByteString::from("a"), + }), + }; + assert_eq!(argument.to_string(), "$a"); + + let argument = ArgumentExpression::Spread { + comments: CommentGroup { comments: vec![] }, + ellipsis: 0, + value: Expression::Variable(Variable { + position: 0, + name: ByteString::from("a"), + }), + }; + assert_eq!(argument.to_string(), "...$a"); + + let argument = ArgumentExpression::ReverseSpread { + comments: CommentGroup { comments: vec![] }, + value: Expression::Variable(Variable { + position: 0, + name: ByteString::from("a"), + }), + ellipsis: 0, + }; + assert_eq!(argument.to_string(), "$a..."); + + let argument = ArgumentExpression::Named { + comments: CommentGroup { comments: vec![] }, + name: Identifier { + position: 0, + value: ByteString::from("a"), + }, + colon: 1, + value: Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("1"), + })), + }; + assert_eq!(argument.to_string(), "a: 1"); + } +} diff --git a/src/tree/expression/array.rs b/src/tree/expression/array.rs index 0e8805e..badaa55 100644 --- a/src/tree/expression/array.rs +++ b/src/tree/expression/array.rs @@ -168,3 +168,149 @@ impl Node for TupleExpression { "tuple expression".to_string() } } + +impl std::fmt::Display for VecExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}[{}]", self.vec, self.elements) + } +} + +impl std::fmt::Display for VecElementExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.value) + } +} + +impl std::fmt::Display for DictExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}[{}]", self.dict, self.elements) + } +} + +impl std::fmt::Display for DictElementExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} => {}", self.key, self.value) + } +} + +impl std::fmt::Display for TupleExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "({})", self.elements) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + use crate::tree::expression::literal::Literal::Integer; + use crate::tree::expression::literal::Literal::String; + use crate::tree::expression::literal::LiteralInteger; + use crate::tree::expression::literal::LiteralString; + use crate::tree::expression::Expression; + + #[test] + fn test_vec_expression_display() { + let vec_expression = VecExpression { + comments: CommentGroup { comments: vec![] }, + vec: Keyword::new(ByteString::from("vec"), 0), + left_bracket: 0, + elements: CommaSeparated { + inner: vec![ + VecElementExpression { + value: Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("1"), + })), + }, + VecElementExpression { + value: Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("2"), + })), + }, + ], + commas: vec![], + }, + right_bracket: 0, + }; + + assert_eq!(vec_expression.to_string(), "vec[1, 2]"); + } + + #[test] + fn test_dict_expression_display() { + let dict_expression = DictExpression { + comments: CommentGroup { comments: vec![] }, + dict: Keyword::new(ByteString::from("dict"), 0), + left_bracket: 0, + elements: CommaSeparated { + inner: vec![ + DictElementExpression { + key: Expression::Literal(String(LiteralString { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("\"a\""), + })), + double_arrow: 0, + value: Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("1"), + })), + }, + DictElementExpression { + key: Expression::Literal(String(LiteralString { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("\"b\""), + })), + double_arrow: 0, + value: Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("2"), + })), + }, + ], + commas: vec![], + }, + right_bracket: 0, + }; + + assert_eq!(dict_expression.to_string(), "dict[\"a\" => 1, \"b\" => 2]"); + } + + #[test] + fn test_tuple_expression_display() { + let tuple_expression = TupleExpression { + comments: CommentGroup { comments: vec![] }, + left_parenthesis: 0, + elements: CommaSeparated { + inner: vec![ + Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("1"), + })), + Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("2"), + })), + Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("3"), + })), + ], + commas: vec![], + }, + right_parenthesis: 0, + }; + + assert_eq!(tuple_expression.to_string(), "(1, 2, 3)"); + } +} diff --git a/src/tree/expression/class.rs b/src/tree/expression/class.rs index a589951..a17eb55 100644 --- a/src/tree/expression/class.rs +++ b/src/tree/expression/class.rs @@ -64,3 +64,103 @@ impl Node for AnonymousClassExpression { "anonymous class expression".to_string() } } + +impl std::fmt::Display for AnonymousClassExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.class)?; + + if !self.arguments.arguments.inner.is_empty() { + write!(f, "{}", self.arguments)?; + } + + if let Some(extends) = &self.extends { + write!(f, " {}", extends)?; + } + + if let Some(implements) = &self.implements { + write!(f, " {}", implements)?; + } + + write!(f, " {}", self.body) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + use crate::tree::expression::argument::ArgumentExpression; + use crate::tree::expression::literal::Literal::Integer; + use crate::tree::expression::literal::LiteralInteger; + use crate::tree::expression::Expression; + use crate::tree::identifier::Identifier; + use crate::tree::identifier::TemplatedIdentifier; + use crate::tree::utils::CommaSeparated; + + #[test] + fn anonymous_class_expression_display() { + let anonymous_class = AnonymousClassExpression { + comments: CommentGroup { comments: vec![] }, + attributes: vec![], + class: Keyword::new(ByteString::from("class"), 0), + arguments: ArgumentListExpression { + comments: CommentGroup { comments: vec![] }, + left_parenthesis: 0, + arguments: CommaSeparated { + inner: vec![ArgumentExpression::Value { + comments: CommentGroup { comments: vec![] }, + value: Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("1"), + })), + }], + commas: vec![], + }, + right_parenthesis: 0, + }, + extends: Some(ClassDefinitionExtends { + extends: Keyword::new(ByteString::from("extends"), 0), + parent: TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + templates: None, + }, + }), + implements: Some(ClassDefinitionImplements { + implements: Keyword::new(ByteString::from("implements"), 0), + interfaces: CommaSeparated { + inner: vec![ + TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Bar"), + }, + templates: None, + }, + TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Baz"), + }, + templates: None, + }, + ], + commas: vec![], + }, + }), + body: ClassDefinitionBody { + left_brace: 0, + members: vec![], + right_brace: 0, + }, + }; + + assert_eq!( + anonymous_class.to_string(), + "class(1) extends Foo implements Bar, Baz { /* ... */ }" + ); + } +} diff --git a/src/tree/expression/construct.rs b/src/tree/expression/construct.rs index d377c7f..1c5a009 100644 --- a/src/tree/expression/construct.rs +++ b/src/tree/expression/construct.rs @@ -66,3 +66,52 @@ impl Node for ExitConstructExpression { "exit construct expression".to_string() } } + +impl std::fmt::Display for ExitConstructExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::Exit { .. } => write!(f, "exit;"), + Self::ExitWith { value, .. } => { + if let Some(value) = value { + write!(f, "exit({});", value) + } else { + write!(f, "exit;") + } + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + use crate::tree::expression::literal::Literal::Integer; + use crate::tree::expression::literal::LiteralInteger; + + #[test] + fn test_exit_display() { + let exit = ExitConstructExpression::Exit { + comments: CommentGroup { comments: vec![] }, + exit: Keyword::new(ByteString::from("exit"), 0), + }; + + assert_eq!(exit.to_string(), "exit;"); + + let value = Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("1"), + })); + + let exit_with = ExitConstructExpression::ExitWith { + comments: CommentGroup { comments: vec![] }, + exit: Keyword::new(ByteString::from("exit"), 0), + left_parenthesis: 0, + value: Some(Box::new(value)), + right_parenthesis: 0, + }; + + assert_eq!(exit_with.to_string(), "exit(1);"); + } +} diff --git a/src/tree/expression/control_flow.rs b/src/tree/expression/control_flow.rs index 92f1321..242f9bc 100644 --- a/src/tree/expression/control_flow.rs +++ b/src/tree/expression/control_flow.rs @@ -134,3 +134,62 @@ impl Node for MatchArmConditionExpression { "match arm condition expression".to_string() } } + +impl std::fmt::Display for MatchExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} ", self.r#match)?; + if let Some(expression) = &self.expression { + write!(f, "{} ", expression)?; + } + write!(f, "{}", self.body) + } +} + +impl std::fmt::Display for MatchBodyExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{{ /* ... */ }}") + } +} + +impl std::fmt::Display for MatchArmExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} => {}", self.condition, self.expression) + } +} + +impl std::fmt::Display for MatchArmConditionExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::Expressions(expressions) => write!(f, "{}", expressions), + Self::Default(default) => write!(f, "{}", default), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + use crate::tree::variable::Variable; + + #[test] + fn test_match_expression_display() { + let expression = Expression::Match(MatchExpression { + comments: CommentGroup { comments: vec![] }, + r#match: Keyword::new(ByteString::from("match"), 0), + expression: Some(Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("a"), + }))), + body: MatchBodyExpression { + left_brace: 0, + arms: CommaSeparated { + inner: vec![], + commas: vec![], + }, + right_brace: 0, + }, + }); + assert_eq!(expression.to_string(), "match $a { /* ... */ }"); + } +} diff --git a/src/tree/expression/function.rs b/src/tree/expression/function.rs index b07d5af..ee2c042 100644 --- a/src/tree/expression/function.rs +++ b/src/tree/expression/function.rs @@ -192,3 +192,169 @@ impl Node for AnonymousFunctionUseClauseVariableExpression { "anonymous function use clause variable expression".to_string() } } + +impl std::fmt::Display for ArrowFunctionExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(r#static) = &self.r#static { + write!(f, "{} ", r#static)?; + } + write!( + f, + "{} {}{} => {};", + self.r#fn, self.parameters, self.return_type, self.body + ) + } +} + +impl std::fmt::Display for AnonymousFunctionExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(r#static) = &self.r#static { + write!(f, "{} ", r#static)?; + } + write!(f, "{} {}", self.function, self.parameters)?; + + if let Some(use_clause) = &self.use_clause { + write!(f, " {}", use_clause)?; + } + + write!(f, "{} {}", self.return_type, self.body) + } +} + +impl std::fmt::Display for AnonymousFunctionUseClauseExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} ({})", self.r#use, self.variables) + } +} + +impl std::fmt::Display for AnonymousFunctionUseClauseVariableExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.variable) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + use crate::tree::definition::function::FunctionLikeParameterDefinition; + use crate::tree::definition::r#type::SignedIntegerTypeDefinition; + use crate::tree::definition::r#type::TypeDefinition; + + #[test] + fn arrow_function_expression_display() { + let arrow_function_expression = ArrowFunctionExpression { + attributes: vec![], + comments: CommentGroup { comments: vec![] }, + r#static: Some(Keyword::new(ByteString::from("static"), 0)), + r#fn: Keyword::new(ByteString::from("fn"), 0), + parameters: FunctionLikeParameterListDefinition { + comments: CommentGroup { comments: vec![] }, + left_parenthesis: 0, + parameters: CommaSeparated { + inner: vec![FunctionLikeParameterDefinition { + attributes: vec![], + comments: CommentGroup { comments: vec![] }, + type_definition: TypeDefinition::SignedInteger( + SignedIntegerTypeDefinition::I64(Keyword::new( + ByteString::from("i64"), + 15, + )), + ), + ellipsis: None, + variable: Variable { + position: 0, + name: ByteString::from("foo"), + }, + default: None, + }], + commas: vec![], + }, + right_parenthesis: 0, + }, + return_type: FunctionLikeReturnTypeDefinition { + colon: 0, + type_definition: TypeDefinition::SignedInteger(SignedIntegerTypeDefinition::I64( + Keyword::new(ByteString::from("i64"), 15), + )), + }, + double_arrow: 0, + body: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + }; + + assert_eq!( + arrow_function_expression.to_string(), + "static fn (i64 $foo): i64 => $foo;" + ); + } + + #[test] + fn anonymous_function_expression_display() { + let anonymous_function_expression = AnonymousFunctionExpression { + attributes: vec![], + comments: CommentGroup { comments: vec![] }, + r#static: Some(Keyword::new(ByteString::from("static"), 0)), + function: Keyword::new(ByteString::from("function"), 0), + parameters: FunctionLikeParameterListDefinition { + comments: CommentGroup { comments: vec![] }, + left_parenthesis: 0, + parameters: CommaSeparated { + inner: vec![FunctionLikeParameterDefinition { + attributes: vec![], + comments: CommentGroup { comments: vec![] }, + type_definition: TypeDefinition::SignedInteger( + SignedIntegerTypeDefinition::I32(Keyword::new( + ByteString::from("i32"), + 15, + )), + ), + ellipsis: None, + variable: Variable { + position: 0, + name: ByteString::from("foo"), + }, + default: None, + }], + commas: vec![], + }, + right_parenthesis: 0, + }, + return_type: FunctionLikeReturnTypeDefinition { + colon: 0, + type_definition: TypeDefinition::SignedInteger(SignedIntegerTypeDefinition::I64( + Keyword::new(ByteString::from("i64"), 15), + )), + }, + use_clause: Some(AnonymousFunctionUseClauseExpression { + comments: CommentGroup { comments: vec![] }, + r#use: Keyword::new(ByteString::from("use"), 0), + variables: CommaSeparated { + inner: vec![AnonymousFunctionUseClauseVariableExpression { + comments: CommentGroup { comments: vec![] }, + variable: Variable { + position: 0, + name: ByteString::from("bar"), + }, + }], + commas: vec![], + }, + left_parenthesis: 0, + right_parenthesis: 0, + }), + body: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + }; + + assert_eq!( + anonymous_function_expression.to_string(), + "static function (i32 $foo) use ($bar): i64 { /* ... */ }" + ); + } +} diff --git a/src/tree/expression/generic.rs b/src/tree/expression/generic.rs index d283afb..0a7906a 100644 --- a/src/tree/expression/generic.rs +++ b/src/tree/expression/generic.rs @@ -33,3 +33,49 @@ impl Node for GenericGroupExpression { "generic group expression".to_string() } } + +impl std::fmt::Display for GenericGroupExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "::<{}>", self.types) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + use crate::tree::identifier::Identifier; + use crate::tree::identifier::TemplatedIdentifier; + + #[test] + fn test_generic_group_expression_display() { + let generic_group_expression = GenericGroupExpression { + double_colon_less_than: 0, + types: CommaSeparated { + inner: vec![ + TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 3, + value: ByteString::from("T"), + }, + templates: None, + }), + TypeDefinition::Identifier(TemplatedIdentifier { + name: Identifier { + position: 3, + value: ByteString::from("P"), + }, + templates: None, + }), + ], + commas: vec![], + }, + greater_than: 0, + }; + + assert_eq!( + format!("{}", generic_group_expression), + "::".to_string() + ); + } +} diff --git a/src/tree/expression/literal.rs b/src/tree/expression/literal.rs index 2a5a9ac..8bb237f 100644 --- a/src/tree/expression/literal.rs +++ b/src/tree/expression/literal.rs @@ -253,3 +253,105 @@ impl Node for LiteralFalse { "literal false expression".to_string() } } + +impl std::fmt::Display for Literal { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Null(inner) => write!(f, "{}", inner), + Self::False(inner) => write!(f, "{}", inner), + Self::True(inner) => write!(f, "{}", inner), + Self::Integer(inner) => write!(f, "{}", inner), + Self::Float(inner) => write!(f, "{}", inner), + Self::String(inner) => write!(f, "{}", inner), + } + } +} + +impl std::fmt::Display for LiteralString { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.value) + } +} + +impl std::fmt::Display for LiteralInteger { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.value) + } +} + +impl std::fmt::Display for LiteralFloat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.value) + } +} + +impl std::fmt::Display for LiteralNull { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "null") + } +} + +impl std::fmt::Display for LiteralTrue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "true") + } +} + +impl std::fmt::Display for LiteralFalse { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "false") + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_literal_display() { + let string = Literal::String(LiteralString { + comments: CommentGroup { comments: vec![] }, + value: ByteString::from("\"foo\""), + position: 0, + }); + + assert_eq!(string.to_string(), "\"foo\""); + + let integer = LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("123"), + }; + + assert_eq!(integer.to_string(), "123"); + + let float = LiteralFloat { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("3.14"), + }; + + assert_eq!(float.to_string(), "3.14"); + + let null = LiteralNull { + comments: CommentGroup { comments: vec![] }, + null: Keyword::new(ByteString::from("null"), 0), + }; + + assert_eq!(null.to_string(), "null"); + + let r#true = LiteralTrue { + comments: CommentGroup { comments: vec![] }, + r#true: Keyword::new(ByteString::from("true"), 0), + }; + + assert_eq!(r#true.to_string(), "true"); + + let r#false = LiteralFalse { + comments: CommentGroup { comments: vec![] }, + r#false: Keyword::new(ByteString::from("false"), 0), + }; + + assert_eq!(r#false.to_string(), "false"); + } +} diff --git a/src/tree/expression/magic_constant.rs b/src/tree/expression/magic_constant.rs index 139013a..3a7d7fe 100644 --- a/src/tree/expression/magic_constant.rs +++ b/src/tree/expression/magic_constant.rs @@ -60,3 +60,32 @@ impl Node for MagicConstant { } } } + +impl std::fmt::Display for MagicConstant { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::Directory { value, .. } + | Self::File { value, .. } + | Self::Line { value, .. } + | Self::Class { value, .. } + | Self::Function { value, .. } + | Self::Method { value, .. } + | Self::Namespace { value, .. } => write!(f, "{}", value), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + + #[test] + fn test_magic_constant_display() { + let magic_constant = MagicConstant::Directory { + position: 0, + value: ByteString::from("__DIR__"), + }; + assert_eq!(magic_constant.to_string(), "__DIR__"); + } +} diff --git a/src/tree/expression/mod.rs b/src/tree/expression/mod.rs index 1960ba7..153fd53 100644 --- a/src/tree/expression/mod.rs +++ b/src/tree/expression/mod.rs @@ -427,3 +427,46 @@ impl Node for Expression { } } } + +impl std::fmt::Display for Expression { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match &self { + Self::Parenthesized(expression) => write!(f, "{}", expression), + Self::ExitConstruct(expression) => write!(f, "{}", expression), + Self::Literal(expression) => write!(f, "{}", expression), + Self::ArithmeticOperation(expression) => write!(f, "{}", expression), + Self::AsyncOperation(expression) => write!(f, "{}", expression), + Self::ArrayOperation(expression) => write!(f, "{}", expression), + Self::AssignmentOperation(expression) => write!(f, "{}", expression), + Self::BitwiseOperation(expression) => write!(f, "{}", expression), + Self::ClassOperation(expression) => write!(f, "{}", expression), + Self::CoalesceOperation(expression) => write!(f, "{}", expression), + Self::ComparisonOperation(expression) => write!(f, "{}", expression), + Self::ExceptionOperation(expression) => write!(f, "{}", expression), + Self::FunctionOperation(expression) => write!(f, "{}", expression), + Self::GeneratorOperation(expression) => write!(f, "{}", expression), + Self::LogicalOperation(expression) => write!(f, "{}", expression), + Self::ObjectOperation(expression) => write!(f, "{}", expression), + Self::RangeOperation(expression) => write!(f, "{}", expression), + Self::StringOperation(expression) => write!(f, "{}", expression), + Self::TypeOperation(expression) => write!(f, "{}", expression), + Self::TernaryOperation(expression) => write!(f, "{}", expression), + Self::Identifier(expression) => write!(f, "{}", expression), + Self::Variable(expression) => write!(f, "{}", expression), + Self::Match(expression) => write!(f, "{}", expression), + Self::AnonymousFunction(expression) => write!(f, "{}", expression), + Self::ArrowFunction(expression) => write!(f, "{}", expression), + Self::Vec(expression) => write!(f, "{}", expression), + Self::Dict(expression) => write!(f, "{}", expression), + Self::Tuple(expression) => write!(f, "{}", expression), + Self::MagicConstant(expression) => write!(f, "{}", expression), + Self::FunctionalOperation(expression) => write!(f, "{}", expression), + } + } +} + +impl std::fmt::Display for ParenthesizedExpression { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "({})", self.expression) + } +} diff --git a/src/tree/expression/operator.rs b/src/tree/expression/operator.rs index ea1fc7a..f684f6f 100644 --- a/src/tree/expression/operator.rs +++ b/src/tree/expression/operator.rs @@ -1888,3 +1888,2030 @@ impl Node for RangeOperationExpression { } } } + +impl std::fmt::Display for ArithmeticOperationExpression { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match &self { + Self::Addition { left, right, .. } => { + write!(f, "{} + {}", left, right) + } + Self::Subtraction { left, right, .. } => { + write!(f, "{} - {}", left, right) + } + Self::Multiplication { left, right, .. } => { + write!(f, "{} * {}", left, right) + } + Self::Division { left, right, .. } => { + write!(f, "{} / {}", left, right) + } + Self::Exponentiation { left, right, .. } => { + write!(f, "{} ** {}", left, right) + } + Self::Modulo { left, right, .. } => { + write!(f, "{} % {}", left, right) + } + Self::Negative { right, .. } => { + write!(f, "-{}", right) + } + Self::Positive { right, .. } => { + write!(f, "+{}", right) + } + Self::PostDecrement { left, .. } => { + write!(f, "{}--", left) + } + Self::PostIncrement { left, .. } => { + write!(f, "{}++", left) + } + Self::PreDecrement { right, .. } => { + write!(f, "--{}", right) + } + Self::PreIncrement { right, .. } => { + write!(f, "++{}", right) + } + } + } +} + +impl std::fmt::Display for ComparisonOperationExpression { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match &self { + Self::Equal { left, right, .. } => { + write!(f, "{} == {}", left, right) + } + Self::Identical { left, right, .. } => { + write!(f, "{} === {}", left, right) + } + Self::NotIdentical { left, right, .. } => { + write!(f, "{} !== {}", left, right) + } + Self::NotEqual { left, right, .. } => { + write!(f, "{} != {}", left, right) + } + Self::LessThan { left, right, .. } => { + write!(f, "{} < {}", left, right) + } + Self::LessThanOrEqual { left, right, .. } => { + write!(f, "{} <= {}", left, right) + } + Self::GreaterThan { left, right, .. } => { + write!(f, "{} > {}", left, right) + } + Self::GreaterThanOrEqual { left, right, .. } => { + write!(f, "{} >= {}", left, right) + } + Self::Spaceship { left, right, .. } => { + write!(f, "{} <=> {}", left, right) + } + } + } +} + +impl std::fmt::Display for LogicalOperationExpression { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match &self { + Self::And { left, right, .. } => { + write!(f, "{} && {}", left, right) + } + Self::Or { left, right, .. } => { + write!(f, "{} || {}", left, right) + } + Self::Not { right, .. } => { + write!(f, "!{}", right) + } + } + } +} + +impl std::fmt::Display for AssignmentOperationExpression { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match &self { + Self::Assignment { left, right, .. } => { + write!(f, "{} = {}", left, right) + } + Self::Addition { left, right, .. } => { + write!(f, "{} += {}", left, right) + } + Self::Subtraction { left, right, .. } => { + write!(f, "{} -= {}", left, right) + } + Self::Multiplication { left, right, .. } => { + write!(f, "{} *= {}", left, right) + } + Self::Division { left, right, .. } => { + write!(f, "{} /= {}", left, right) + } + Self::Exponentiation { left, right, .. } => { + write!(f, "{} **= {}", left, right) + } + Self::Modulo { left, right, .. } => { + write!(f, "{} %= {}", left, right) + } + Self::Concat { left, right, .. } => { + write!(f, "{} .= {}", left, right) + } + Self::BitwiseAnd { left, right, .. } => { + write!(f, "{} &= {}", left, right) + } + Self::BitwiseOr { left, right, .. } => { + write!(f, "{} |= {}", left, right) + } + Self::BitwiseXor { left, right, .. } => { + write!(f, "{} ^= {}", left, right) + } + Self::LeftShift { left, right, .. } => { + write!(f, "{} <<= {}", left, right) + } + Self::RightShift { left, right, .. } => { + write!(f, "{} >>= {}", left, right) + } + Self::Coalesce { left, right, .. } => { + write!(f, "{} ??= {}", left, right) + } + } + } +} + +impl std::fmt::Display for BitwiseOperationExpression { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match &self { + Self::LeftShift { left, right, .. } => { + write!(f, "{} << {}", left, right) + } + Self::RightShift { left, right, .. } => { + write!(f, "{} >> {}", left, right) + } + Self::And { left, right, .. } => { + write!(f, "{} & {}", left, right) + } + Self::Or { left, right, .. } => { + write!(f, "{} | {}", left, right) + } + Self::Xor { left, right, .. } => { + write!(f, "{} ^ {}", left, right) + } + Self::Not { right, .. } => { + write!(f, "~{}", right) + } + } + } +} + +impl std::fmt::Display for AsyncOperationExpression { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match &self { + Self::Await { + r#await, + expression, + .. + } => { + write!(f, "{} {}", r#await, expression) + } + Self::Async { + r#async, + expression, + .. + } => { + write!(f, "{} {}", r#async, expression) + } + Self::Concurrently { concurrently, .. } => { + write!(f, "{} {{{{ /* ... */ }}}}", concurrently) + } + } + } +} + +impl std::fmt::Display for RangeOperationExpression { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match &self { + Self::Between { from, to, .. } => { + write!(f, "{}..{}", from, to) + } + Self::BetweenInclusive { from, to, .. } => { + write!(f, "{}..={}", from, to) + } + Self::To { to, .. } => { + write!(f, "..{}", to) + } + Self::ToInclusive { to, .. } => { + write!(f, "..={}", to) + } + Self::From { from, .. } => { + write!(f, "{}..", from) + } + Self::Full { .. } => { + write!(f, "..") + } + } + } +} + +impl std::fmt::Display for FunctionalOperationExpression { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match &self { + Self::Expression { + generics, + expression, + .. + } => { + write!(f, "$(")?; + if let Some(generics) = generics { + write!(f, "{}", generics)?; + } + write!(f, "{}", expression)?; + write!(f, ")") + } + Self::Pipe { left, right, .. } => write!(f, "{} |> {}", left, right), + } + } +} + +impl std::fmt::Display for StringOperationExpression { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match &self { + Self::Concat { left, right, .. } => { + write!(f, "{}.{}", left, right) + } + } + } +} + +impl std::fmt::Display for ArrayOperationExpression { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match &self { + Self::Access { array, index, .. } => { + write!(f, "{}[{}]", array, index) + } + Self::Push { array, .. } => { + write!(f, "{}[]", array) + } + Self::Unset { item, .. } => { + write!(f, "unset {}", item) + } + Self::Isset { item, .. } => { + write!(f, "isset {}", item) + } + Self::In { item, array, .. } => { + write!(f, "{} in {}", item, array) + } + } + } +} + +impl std::fmt::Display for CoalesceOperationExpression { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match &self { + Self::Coalesce { left, right, .. } => { + write!(f, "{} ?? {}", left, right) + } + } + } +} + +impl std::fmt::Display for TernaryOperationExpression { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match &self { + Self::Ternary { + condition, + if_true, + if_false, + .. + } => { + write!(f, "{} ? {} : {}", condition, if_true, if_false) + } + Self::ShortTernary { + condition, + if_false, + .. + } => { + write!(f, "{} ?: {}", condition, if_false) + } + Self::ImplicitShortTernary { + condition, + if_false, + .. + } => { + write!(f, "{} ? : {}", condition, if_false) + } + } + } +} + +impl std::fmt::Display for TypeOperationExpression { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match &self { + Self::Instanceof { left, right, .. } => { + write!(f, "{} instanceof {}", left, right) + } + Self::Is { left, right, .. } => { + write!(f, "{} is {}", left, right) + } + Self::Into { left, right, .. } => { + write!(f, "{} into {}", left, right) + } + Self::As { left, right, .. } => { + write!(f, "{} as {}", left, right) + } + } + } +} + +impl std::fmt::Display for GeneratorOperationExpression { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match &self { + Self::Yield { .. } => { + write!(f, "yield") + } + Self::YieldFrom { value, .. } => { + write!(f, "yield from {}", value) + } + Self::YieldValue { value, .. } => { + write!(f, "yield {}", value) + } + Self::YieldKeyValue { key, value, .. } => { + write!(f, "yield {} => {}", key, value) + } + } + } +} + +impl std::fmt::Display for ExceptionOperationExpression { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match &self { + Self::Throw { value, .. } => { + write!(f, "throw {}", value) + } + } + } +} + +impl std::fmt::Display for ObjectOperationExpression { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match &self { + Self::Clone { object, .. } => { + write!(f, "clone {}", object) + } + Self::MethodCall { + object, + method, + generics, + arguments, + .. + } => { + write!(f, "{}->{}", object, method)?; + if let Some(generics) = generics { + write!(f, "{}", generics)?; + } + write!(f, "{}", arguments) + } + Self::NullsafeMethodCall { + object, + method, + generics, + arguments, + .. + } => { + write!(f, "{}?->{}", object, method)?; + if let Some(generics) = generics { + write!(f, "{}", generics)?; + } + write!(f, "{}", arguments) + } + Self::MethodClosureCreation { + object, + method, + generics, + placeholder, + .. + } => { + write!(f, "{}->{}", object, method)?; + if let Some(generics) = generics { + write!(f, "{}", generics)?; + } + write!(f, "{}", placeholder) + } + Self::PropertyFetch { + object, property, .. + } => { + write!(f, "{}->{}", object, property) + } + Self::NullsafePropertyFetch { + object, property, .. + } => { + write!(f, "{}?->{}", object, property) + } + } + } +} + +impl std::fmt::Display for ClassOperationInitializationClassExpression { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match &self { + Self::Identifier(identifier) => { + write!(f, "new {}", identifier) + } + Self::Variable(variable) => { + write!(f, "new {}", variable) + } + } + } +} + +impl std::fmt::Display for ClassOperationExpression { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match &self { + Self::Initialization { + class, + generics, + arguments, + .. + } => { + write!(f, "{}", class)?; + if let Some(generics) = generics { + write!(f, "{}", generics)?; + } + write!(f, "{}", arguments) + } + Self::AnonymousInitialization { class, .. } => write!(f, "new {}", class), + Self::StaticMethodCall { + class, + method, + generics, + arguments, + .. + } => { + write!(f, "{}::{}", class, method)?; + if let Some(generics) = generics { + write!(f, "{}", generics)?; + } + write!(f, "{}", arguments) + } + Self::StaticMethodClosureCreation { + class, + method, + generics, + placeholder, + .. + } => { + write!(f, "{}::{}", class, method)?; + if let Some(generics) = generics { + write!(f, "{}", generics)?; + } + write!(f, "{}", placeholder) + } + Self::StaticPropertyFetch { + class, property, .. + } => { + write!(f, "{}::{}", class, property) + } + Self::ConstantFetch { + class, constant, .. + } => { + write!(f, "{}::{}", class, constant) + } + } + } +} + +impl std::fmt::Display for FunctionOperationExpression { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match &self { + Self::Call { + function, + generics, + arguments, + .. + } => { + write!(f, "{}", function)?; + if let Some(generics) = generics { + write!(f, "{}", generics)?; + } + write!(f, "{}", arguments) + } + Self::ClosureCreation { + function, + generics, + placeholder, + .. + } => { + write!(f, "{}", function)?; + if let Some(generics) = generics { + write!(f, "{}", generics)?; + } + write!(f, "{}", placeholder) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + use crate::tree::definition::class::ClassDefinitionBody; + use crate::tree::definition::r#type::SignedIntegerTypeDefinition; + use crate::tree::expression::argument::ArgumentExpression; + use crate::tree::expression::literal::Literal::Integer; + use crate::tree::expression::literal::LiteralInteger; + + #[test] + fn test_functional_operation_expression_display() { + let pipe = FunctionalOperationExpression::Pipe { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + pipe: 0, + greater_than: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(pipe.to_string(), "$foo |> $bar"); + + let expression = FunctionalOperationExpression::Expression { + comments: CommentGroup { comments: vec![] }, + dollar: 0, + generics: None, + left_parenthesis: 0, + expression: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right_parenthesis: 0, + }; + + assert_eq!(expression.to_string(), "$($foo)"); + } + + #[test] + fn test_assignment_operation_expression_display() { + let assignment = AssignmentOperationExpression::Assignment { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + equals: 0, + }; + + assert_eq!(assignment.to_string(), "$foo = $bar"); + + let addition = AssignmentOperationExpression::Addition { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + plus_equals: 0, + }; + + assert_eq!(addition.to_string(), "$foo += $bar"); + + let subtraction = AssignmentOperationExpression::Subtraction { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + minus_equals: 0, + }; + + assert_eq!(subtraction.to_string(), "$foo -= $bar"); + + let multiplication = AssignmentOperationExpression::Multiplication { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + asterisk_equals: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(multiplication.to_string(), "$foo *= $bar"); + + let division = AssignmentOperationExpression::Division { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + slash_equals: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(division.to_string(), "$foo /= $bar"); + + let modulo = AssignmentOperationExpression::Modulo { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + percent_equals: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(modulo.to_string(), "$foo %= $bar"); + + let bitwise_and = AssignmentOperationExpression::BitwiseAnd { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + ampersand_equals: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(bitwise_and.to_string(), "$foo &= $bar"); + + let bitwise_or = AssignmentOperationExpression::BitwiseOr { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + pipe_equals: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(bitwise_or.to_string(), "$foo |= $bar"); + + let bitwise_xor = AssignmentOperationExpression::BitwiseXor { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + caret_equals: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(bitwise_xor.to_string(), "$foo ^= $bar"); + + let left_shift = AssignmentOperationExpression::LeftShift { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + left_shift_equals: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(left_shift.to_string(), "$foo <<= $bar"); + + let right_shift = AssignmentOperationExpression::RightShift { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right_shift_equals: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(right_shift.to_string(), "$foo >>= $bar"); + + let exponentiation = AssignmentOperationExpression::Exponentiation { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + pow_equals: 0, + }; + + assert_eq!(exponentiation.to_string(), "$foo **= $bar"); + + let concat = AssignmentOperationExpression::Concat { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + dot_equals: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(concat.to_string(), "$foo .= $bar"); + + let coalesce = AssignmentOperationExpression::Coalesce { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + coalesce_equals: 0, + }; + + assert_eq!(coalesce.to_string(), "$foo ??= $bar"); + } + + #[test] + fn test_arithmetic_operation_expression_display() { + let addition = ArithmeticOperationExpression::Addition { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + plus: 0, + }; + + assert_eq!(addition.to_string(), "$foo + $bar"); + + let subtraction = ArithmeticOperationExpression::Subtraction { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + minus: 0, + }; + + assert_eq!(subtraction.to_string(), "$foo - $bar"); + + let multiplication = ArithmeticOperationExpression::Multiplication { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + asterisk: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(multiplication.to_string(), "$foo * $bar"); + + let division = ArithmeticOperationExpression::Division { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + slash: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(division.to_string(), "$foo / $bar"); + + let modulo = ArithmeticOperationExpression::Modulo { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + percent: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(modulo.to_string(), "$foo % $bar"); + + let exponentiation = ArithmeticOperationExpression::Exponentiation { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + pow: 0, + }; + + assert_eq!(exponentiation.to_string(), "$foo ** $bar"); + + let negative = ArithmeticOperationExpression::Negative { + comments: CommentGroup { comments: vec![] }, + minus: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(negative.to_string(), "-$bar"); + + let positive = ArithmeticOperationExpression::Positive { + comments: CommentGroup { comments: vec![] }, + plus: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(positive.to_string(), "+$bar"); + + let pre_increment = ArithmeticOperationExpression::PreIncrement { + comments: CommentGroup { comments: vec![] }, + increment: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(pre_increment.to_string(), "++$bar"); + + let pre_decrement = ArithmeticOperationExpression::PreDecrement { + comments: CommentGroup { comments: vec![] }, + decrement: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(pre_decrement.to_string(), "--$bar"); + + let post_increment = ArithmeticOperationExpression::PostIncrement { + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + increment: 0, + }; + + assert_eq!(post_increment.to_string(), "$bar++"); + + let post_decrement = ArithmeticOperationExpression::PostDecrement { + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + decrement: 0, + }; + + assert_eq!(post_decrement.to_string(), "$bar--"); + } + + #[test] + fn test_bitwise_operation_expression_display() { + let bitwise_and = BitwiseOperationExpression::And { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + and: 0, + }; + + assert_eq!(bitwise_and.to_string(), "$foo & $bar"); + + let bitwise_or = BitwiseOperationExpression::Or { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + or: 0, + }; + + assert_eq!(bitwise_or.to_string(), "$foo | $bar"); + + let bitwise_xor = BitwiseOperationExpression::Xor { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + xor: 0, + }; + + assert_eq!(bitwise_xor.to_string(), "$foo ^ $bar"); + + let bitwise_not = BitwiseOperationExpression::Not { + comments: CommentGroup { comments: vec![] }, + not: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(bitwise_not.to_string(), "~$bar"); + + let bitwise_left_shift = BitwiseOperationExpression::LeftShift { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + left_shift: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(bitwise_left_shift.to_string(), "$foo << $bar"); + + let bitwise_right_shift = BitwiseOperationExpression::RightShift { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right_shift: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(bitwise_right_shift.to_string(), "$foo >> $bar"); + } + + #[test] + fn test_comparison_operation_expression_display() { + let equal = ComparisonOperationExpression::Equal { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + double_equals: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(equal.to_string(), "$foo == $bar"); + + let identical = ComparisonOperationExpression::Identical { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + triple_equals: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(identical.to_string(), "$foo === $bar"); + + let not_equal = ComparisonOperationExpression::NotEqual { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + bang_equals: 0, + }; + + assert_eq!(not_equal.to_string(), "$foo != $bar"); + + let not_identical = ComparisonOperationExpression::NotIdentical { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + bang_double_equals: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(not_identical.to_string(), "$foo !== $bar"); + + let less_than = ComparisonOperationExpression::LessThan { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + less_than: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(less_than.to_string(), "$foo < $bar"); + + let greater_than = ComparisonOperationExpression::GreaterThan { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + greater_than: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(greater_than.to_string(), "$foo > $bar"); + + let less_than_or_equal = ComparisonOperationExpression::LessThanOrEqual { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + less_than_equals: 0, + }; + + assert_eq!(less_than_or_equal.to_string(), "$foo <= $bar"); + + let greater_than_or_equal = ComparisonOperationExpression::GreaterThanOrEqual { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + greater_than_equals: 0, + }; + + assert_eq!(greater_than_or_equal.to_string(), "$foo >= $bar"); + + let spaceship = ComparisonOperationExpression::Spaceship { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + spaceship: 0, + }; + + assert_eq!(spaceship.to_string(), "$foo <=> $bar"); + } + + #[test] + fn test_logical_operation_expression_display() { + let logical_and = LogicalOperationExpression::And { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + double_ampersand: 0, + }; + + assert_eq!(logical_and.to_string(), "$foo && $bar"); + + let logical_or = LogicalOperationExpression::Or { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + double_pipe: 0, + }; + + assert_eq!(logical_or.to_string(), "$foo || $bar"); + + let logical_not = LogicalOperationExpression::Not { + comments: CommentGroup { comments: vec![] }, + bang: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + }; + + assert_eq!(logical_not.to_string(), "!$foo"); + } + + #[test] + fn test_string_operation_expression_display() { + let concat = StringOperationExpression::Concat { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + dot: 0, + }; + + assert_eq!(concat.to_string(), "$foo.$bar"); + } + + #[test] + fn test_array_operation_expression_display() { + let access = ArrayOperationExpression::Access { + comments: CommentGroup { comments: vec![] }, + array: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + left_bracket: 0, + index: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + right_bracket: 0, + }; + + assert_eq!(access.to_string(), "$foo[$bar]"); + + let push = ArrayOperationExpression::Push { + comments: CommentGroup { comments: vec![] }, + array: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + left_bracket: 0, + right_bracket: 0, + }; + + assert_eq!(push.to_string(), "$foo[]"); + + let unset = ArrayOperationExpression::Unset { + comments: CommentGroup { comments: vec![] }, + unset: Keyword::new(ByteString::from("unset"), 0), + item: Box::new(Expression::ArrayOperation( + ArrayOperationExpression::Access { + comments: CommentGroup { comments: vec![] }, + array: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + left_bracket: 0, + index: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + right_bracket: 0, + }, + )), + }; + + assert_eq!(unset.to_string(), "unset $foo[$bar]"); + + let isset = ArrayOperationExpression::Isset { + comments: CommentGroup { comments: vec![] }, + isset: Keyword::new(ByteString::from("isset"), 0), + item: Box::new(Expression::ArrayOperation( + ArrayOperationExpression::Access { + comments: CommentGroup { comments: vec![] }, + array: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + left_bracket: 0, + index: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + right_bracket: 0, + }, + )), + }; + + assert_eq!(isset.to_string(), "isset $foo[$bar]"); + + let r#in = ArrayOperationExpression::In { + comments: CommentGroup { comments: vec![] }, + item: Box::new(Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("1"), + }))), + r#in: Keyword::new(ByteString::from("in"), 0), + array: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + }; + + assert_eq!(r#in.to_string(), "1 in $foo"); + } + + #[test] + fn test_coalesce_operation_expression_display() { + let coalesce = CoalesceOperationExpression::Coalesce { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + double_question: 0, + right: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(coalesce.to_string(), "$foo ?? $bar"); + } + + #[test] + fn test_ternary_operation_expression_display() { + let ternary = TernaryOperationExpression::Ternary { + comments: CommentGroup { comments: vec![] }, + condition: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + question: 0, + if_true: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + colon: 0, + if_false: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("baz"), + })), + }; + + assert_eq!(ternary.to_string(), "$foo ? $bar : $baz"); + + let short_ternary = TernaryOperationExpression::ShortTernary { + comments: CommentGroup { comments: vec![] }, + condition: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + if_false: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + question_colon: 0, + }; + + assert_eq!(short_ternary.to_string(), "$foo ?: $bar"); + + let implicit_short_ternary = TernaryOperationExpression::ImplicitShortTernary { + comments: CommentGroup { comments: vec![] }, + condition: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + question: 0, + colon: 0, + if_false: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(implicit_short_ternary.to_string(), "$foo ? : $bar"); + } + + #[test] + fn test_type_operation_expression_display() { + let instance_of = TypeOperationExpression::Instanceof { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + instanceof: Keyword::new(ByteString::from("instanceof"), 0), + right: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + }; + + assert_eq!(instance_of.to_string(), "$foo instanceof Foo"); + + let is = TypeOperationExpression::Is { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + is: Keyword::new(ByteString::from("is"), 0), + right: TypeDefinition::SignedInteger(SignedIntegerTypeDefinition::I64(Keyword::new( + ByteString::from("i64"), + 0, + ))), + }; + + assert_eq!(is.to_string(), "$foo is i64"); + + let into = TypeOperationExpression::Into { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + into: Keyword::new(ByteString::from("into"), 0), + right: TypeDefinition::SignedInteger(SignedIntegerTypeDefinition::I64(Keyword::new( + ByteString::from("i64"), + 0, + ))), + }; + + assert_eq!(into.to_string(), "$foo into i64"); + + let r#as = TypeOperationExpression::As { + comments: CommentGroup { comments: vec![] }, + left: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + r#as: Keyword::new(ByteString::from("as"), 0), + right: TypeDefinition::SignedInteger(SignedIntegerTypeDefinition::I64(Keyword::new( + ByteString::from("i64"), + 0, + ))), + }; + + assert_eq!(r#as.to_string(), "$foo as i64"); + } + + #[test] + fn test_generator_operation_expression_display() { + let r#yield = GeneratorOperationExpression::Yield { + comments: CommentGroup { comments: vec![] }, + r#yield: Keyword::new(ByteString::from("yield"), 0), + }; + + assert_eq!(r#yield.to_string(), "yield"); + + let yield_value = GeneratorOperationExpression::YieldValue { + comments: CommentGroup { comments: vec![] }, + r#yield: Keyword::new(ByteString::from("yield"), 0), + value: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + }; + + assert_eq!(yield_value.to_string(), "yield $foo"); + + let yield_key_value = GeneratorOperationExpression::YieldKeyValue { + comments: CommentGroup { comments: vec![] }, + r#yield: Keyword::new(ByteString::from("yield"), 0), + key: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + double_arrow: 0, + value: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("bar"), + })), + }; + + assert_eq!(yield_key_value.to_string(), "yield $foo => $bar"); + + let yield_from = GeneratorOperationExpression::YieldFrom { + comments: CommentGroup { comments: vec![] }, + r#yield: Keyword::new(ByteString::from("yield"), 0), + from: Keyword::new(ByteString::from("from"), 0), + value: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + }; + + assert_eq!(yield_from.to_string(), "yield from $foo"); + } + + #[test] + fn test_exception_operation_expression_display() { + let throw = ExceptionOperationExpression::Throw { + comments: CommentGroup { comments: vec![] }, + r#throw: Keyword::new(ByteString::from("throw"), 0), + value: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + }; + + assert_eq!(throw.to_string(), "throw $foo"); + } + + #[test] + fn test_object_operation_expression_display() { + let clone = ObjectOperationExpression::Clone { + comments: CommentGroup { comments: vec![] }, + clone: Keyword::new(ByteString::from("clone"), 0), + object: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + }; + + assert_eq!(clone.to_string(), "clone $foo"); + + let method_call = ObjectOperationExpression::MethodCall { + comments: CommentGroup { comments: vec![] }, + object: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + arrow: 0, + method: Identifier { + position: 0, + value: ByteString::from("bar"), + }, + generics: None, + arguments: ArgumentListExpression { + comments: CommentGroup { comments: vec![] }, + left_parenthesis: 0, + arguments: CommaSeparated { + inner: vec![ArgumentExpression::Value { + comments: CommentGroup { comments: vec![] }, + value: Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("1"), + })), + }], + commas: vec![], + }, + right_parenthesis: 0, + }, + }; + + assert_eq!(method_call.to_string(), "$foo->bar(1)"); + + let nullsafe_method_call = ObjectOperationExpression::NullsafeMethodCall { + comments: CommentGroup { comments: vec![] }, + object: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + question_arrow: 0, + method: Identifier { + position: 0, + value: ByteString::from("bar"), + }, + generics: None, + arguments: ArgumentListExpression { + comments: CommentGroup { comments: vec![] }, + left_parenthesis: 0, + arguments: CommaSeparated { + inner: vec![ArgumentExpression::Value { + comments: CommentGroup { comments: vec![] }, + value: Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("1"), + })), + }], + commas: vec![], + }, + right_parenthesis: 0, + }, + }; + + assert_eq!(nullsafe_method_call.to_string(), "$foo?->bar(1)"); + + let method_closure_creation = ObjectOperationExpression::MethodClosureCreation { + comments: CommentGroup { comments: vec![] }, + object: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + arrow: 0, + method: Identifier { + position: 0, + value: ByteString::from("bar"), + }, + generics: None, + placeholder: ArgumentPlaceholderExpression { + comments: CommentGroup { comments: vec![] }, + left_parenthesis: 0, + ellipsis: 0, + right_parenthesis: 0, + }, + }; + + assert_eq!(method_closure_creation.to_string(), "$foo->bar(...)"); + + let property_fetch = ObjectOperationExpression::PropertyFetch { + comments: CommentGroup { comments: vec![] }, + object: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + arrow: 0, + property: Identifier { + position: 0, + value: ByteString::from("bar"), + }, + }; + + assert_eq!(property_fetch.to_string(), "$foo->bar"); + + let nullsafe_property_fetch = ObjectOperationExpression::NullsafePropertyFetch { + comments: CommentGroup { comments: vec![] }, + object: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + question_arrow: 0, + property: Identifier { + position: 0, + value: ByteString::from("bar"), + }, + }; + + assert_eq!(nullsafe_property_fetch.to_string(), "$foo?->bar"); + } + + #[test] + fn test_class_operation_initialization_class_expression_display() { + let identifier = ClassOperationInitializationClassExpression::Identifier(Identifier { + position: 0, + value: ByteString::from("Foo"), + }); + + assert_eq!(identifier.to_string(), "new Foo"); + + let variable = ClassOperationInitializationClassExpression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + }); + + assert_eq!(variable.to_string(), "new $foo"); + } + + #[test] + fn test_class_operation_expression() { + let initialization = ClassOperationExpression::Initialization { + comments: CommentGroup { comments: vec![] }, + new: Keyword::new(ByteString::from("new"), 0), + class: ClassOperationInitializationClassExpression::Identifier(Identifier { + position: 0, + value: ByteString::from("Foo"), + }), + generics: None, + arguments: ArgumentListExpression { + comments: CommentGroup { comments: vec![] }, + left_parenthesis: 0, + arguments: CommaSeparated { + inner: vec![ArgumentExpression::Value { + comments: CommentGroup { comments: vec![] }, + value: Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("1"), + })), + }], + commas: vec![], + }, + right_parenthesis: 0, + }, + }; + + assert_eq!(initialization.to_string(), "new Foo(1)"); + + let anonymous_initialization = ClassOperationExpression::AnonymousInitialization { + comments: CommentGroup { comments: vec![] }, + new: Keyword::new(ByteString::from("new"), 0), + class: AnonymousClassExpression { + comments: CommentGroup { comments: vec![] }, + attributes: vec![], + class: Keyword::new(ByteString::from("class"), 0), + arguments: ArgumentListExpression { + comments: CommentGroup { comments: vec![] }, + left_parenthesis: 0, + arguments: CommaSeparated { + inner: vec![], + commas: vec![], + }, + right_parenthesis: 0, + }, + extends: None, + implements: None, + body: ClassDefinitionBody { + left_brace: 0, + members: vec![], + right_brace: 0, + }, + }, + }; + + assert_eq!( + anonymous_initialization.to_string(), + "new class { /* ... */ }" + ); + + let anonymous_initialization_with_argument = + ClassOperationExpression::AnonymousInitialization { + comments: CommentGroup { comments: vec![] }, + new: Keyword::new(ByteString::from("new"), 0), + class: AnonymousClassExpression { + comments: CommentGroup { comments: vec![] }, + attributes: vec![], + class: Keyword::new(ByteString::from("class"), 0), + arguments: ArgumentListExpression { + comments: CommentGroup { comments: vec![] }, + left_parenthesis: 0, + arguments: CommaSeparated { + inner: vec![ArgumentExpression::Value { + comments: CommentGroup { comments: vec![] }, + value: Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("1"), + })), + }], + commas: vec![], + }, + right_parenthesis: 0, + }, + extends: None, + implements: None, + body: ClassDefinitionBody { + left_brace: 0, + members: vec![], + right_brace: 0, + }, + }, + }; + + assert_eq!( + anonymous_initialization_with_argument.to_string(), + "new class(1) { /* ... */ }" + ); + + let static_method_call = ClassOperationExpression::StaticMethodCall { + comments: CommentGroup { comments: vec![] }, + class: Box::new(Expression::Identifier(Identifier { + position: 0, + value: ByteString::from("Foo"), + })), + double_colon: 0, + method: Identifier { + position: 0, + value: ByteString::from("bar"), + }, + generics: None, + arguments: ArgumentListExpression { + comments: CommentGroup { comments: vec![] }, + left_parenthesis: 0, + arguments: CommaSeparated { + inner: vec![ArgumentExpression::Value { + comments: CommentGroup { comments: vec![] }, + value: Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("1"), + })), + }], + commas: vec![], + }, + right_parenthesis: 0, + }, + }; + + assert_eq!(static_method_call.to_string(), "Foo::bar(1)"); + + let static_method_closure = ClassOperationExpression::StaticMethodClosureCreation { + comments: CommentGroup { comments: vec![] }, + class: Box::new(Expression::Identifier(Identifier { + position: 0, + value: ByteString::from("Foo"), + })), + double_colon: 0, + method: Identifier { + position: 0, + value: ByteString::from("bar"), + }, + generics: None, + placeholder: ArgumentPlaceholderExpression { + comments: CommentGroup { comments: vec![] }, + left_parenthesis: 0, + ellipsis: 0, + right_parenthesis: 0, + }, + }; + + assert_eq!(static_method_closure.to_string(), "Foo::bar(...)"); + + let static_property_fetch = ClassOperationExpression::StaticPropertyFetch { + comments: CommentGroup { comments: vec![] }, + class: Box::new(Expression::Identifier(Identifier { + position: 0, + value: ByteString::from("Foo"), + })), + double_colon: 0, + property: Variable { + position: 0, + name: ByteString::from("bar"), + }, + }; + + assert_eq!(static_property_fetch.to_string(), "Foo::$bar"); + + let constant_fetch = ClassOperationExpression::ConstantFetch { + comments: CommentGroup { comments: vec![] }, + class: Box::new(Expression::Identifier(Identifier { + position: 0, + value: ByteString::from("Foo"), + })), + double_colon: 0, + constant: Identifier { + position: 0, + value: ByteString::from("BAR"), + }, + }; + + assert_eq!(constant_fetch.to_string(), "Foo::BAR"); + } + + #[test] + fn test_function_operation_expression() { + let function_call = FunctionOperationExpression::Call { + comments: CommentGroup { comments: vec![] }, + function: Box::new(Expression::Identifier(Identifier { + position: 0, + value: ByteString::from("foo"), + })), + generics: None, + arguments: ArgumentListExpression { + comments: CommentGroup { comments: vec![] }, + left_parenthesis: 0, + arguments: CommaSeparated { + inner: vec![ArgumentExpression::Value { + comments: CommentGroup { comments: vec![] }, + value: Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("1"), + })), + }], + commas: vec![], + }, + right_parenthesis: 0, + }, + }; + + assert_eq!(function_call.to_string(), "foo(1)"); + + let function_closure = FunctionOperationExpression::ClosureCreation { + comments: CommentGroup { comments: vec![] }, + function: Box::new(Expression::Identifier(Identifier { + position: 0, + value: ByteString::from("foo"), + })), + generics: None, + placeholder: ArgumentPlaceholderExpression { + comments: CommentGroup { comments: vec![] }, + left_parenthesis: 0, + ellipsis: 0, + right_parenthesis: 0, + }, + }; + + assert_eq!(function_closure.to_string(), "foo(...)"); + } + + #[test] + fn test_async_operation_expression() { + let r#async = AsyncOperationExpression::Async { + comments: CommentGroup { comments: vec![] }, + r#async: Keyword::new(ByteString::from("async"), 0), + expression: Box::new(Expression::FunctionOperation( + FunctionOperationExpression::Call { + comments: CommentGroup { comments: vec![] }, + function: Box::new(Expression::Identifier(Identifier { + position: 0, + value: ByteString::from("foo"), + })), + generics: None, + arguments: ArgumentListExpression { + comments: CommentGroup { comments: vec![] }, + left_parenthesis: 0, + arguments: CommaSeparated { + inner: vec![ArgumentExpression::Value { + comments: CommentGroup { comments: vec![] }, + value: Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("1"), + })), + }], + commas: vec![], + }, + right_parenthesis: 0, + }, + }, + )), + }; + + assert_eq!(r#async.to_string(), "async foo(1)"); + + let r#await = AsyncOperationExpression::Await { + comments: CommentGroup { comments: vec![] }, + r#await: Keyword::new(ByteString::from("await"), 0), + expression: Box::new(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + }; + + assert_eq!(r#await.to_string(), "await $foo"); + + let concurrently = AsyncOperationExpression::Concurrently { + comments: CommentGroup { comments: vec![] }, + concurrently: Keyword::new(ByteString::from("concurrently"), 0), + left_brace: 0, + expressions: CommaSeparated { + inner: vec![], + commas: vec![], + }, + right_brace: 0, + }; + + assert_eq!(concurrently.to_string(), "concurrently {{ /* ... */ }}"); + } + + #[test] + fn test_range_operation_expression() { + let between = RangeOperationExpression::Between { + comments: CommentGroup { comments: vec![] }, + from: Box::new(Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("1"), + }))), + double_dot: 0, + to: Box::new(Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("10"), + }))), + }; + + assert_eq!(between.to_string(), "1..10"); + + let between_inclusive = RangeOperationExpression::BetweenInclusive { + comments: CommentGroup { comments: vec![] }, + from: Box::new(Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("1"), + }))), + double_dot: 0, + to: Box::new(Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("10"), + }))), + equals: 0, + }; + + assert_eq!(between_inclusive.to_string(), "1..=10"); + + let to = RangeOperationExpression::To { + comments: CommentGroup { comments: vec![] }, + double_dot: 0, + to: Box::new(Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("10"), + }))), + }; + + assert_eq!(to.to_string(), "..10"); + + let to_inclusive = RangeOperationExpression::ToInclusive { + comments: CommentGroup { comments: vec![] }, + double_dot: 0, + to: Box::new(Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("10"), + }))), + equals: 0, + }; + + assert_eq!(to_inclusive.to_string(), "..=10"); + + let from = RangeOperationExpression::From { + comments: CommentGroup { comments: vec![] }, + from: Box::new(Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("10"), + }))), + double_dot: 0, + }; + + assert_eq!(from.to_string(), "10.."); + + let full = RangeOperationExpression::Full { + comments: CommentGroup { comments: vec![] }, + double_dot: 0, + }; + + assert_eq!(full.to_string(), ".."); + } +} diff --git a/src/tree/identifier.rs b/src/tree/identifier.rs index 287fc55..2f4faec 100644 --- a/src/tree/identifier.rs +++ b/src/tree/identifier.rs @@ -87,3 +87,56 @@ impl std::fmt::Display for TemplatedIdentifier { Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::tree::comment::CommentGroup; + use crate::tree::definition::r#type::SignedIntegerTypeDefinition; + use crate::tree::definition::r#type::TypeDefinition; + use crate::tree::token::Keyword; + use crate::tree::utils::CommaSeparated; + + #[test] + fn test_identifier_display() { + let identifier = Identifier { + position: 0, + value: ByteString::from("Foo"), + }; + + assert_eq!(identifier.to_string(), "Foo"); + } + + #[test] + fn test_templated_identifier_display() { + let identifier = TemplatedIdentifier { + name: Identifier { + position: 0, + value: ByteString::from("Foo"), + }, + templates: None, + }; + + assert_eq!(identifier.to_string(), "Foo"); + + let identifier = TemplatedIdentifier { + name: Identifier { + position: 5, + value: ByteString::from("Foo"), + }, + templates: Some(TypeTemplateGroupDefinition { + comments: CommentGroup { comments: vec![] }, + less_than: 10, + members: CommaSeparated { + inner: vec![TypeDefinition::SignedInteger( + SignedIntegerTypeDefinition::I64(Keyword::new(ByteString::from("i64"), 15)), + )], + commas: vec![1], + }, + greater_than: 20, + }), + }; + + assert_eq!(identifier.to_string(), "Foo"); + } +} diff --git a/src/tree/statement/block.rs b/src/tree/statement/block.rs index 6622b6e..9d5f287 100644 --- a/src/tree/statement/block.rs +++ b/src/tree/statement/block.rs @@ -41,3 +41,9 @@ impl Node for BlockStatement { "block statement".to_string() } } + +impl std::fmt::Display for BlockStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{{ /* ... */ }}") + } +} diff --git a/src/tree/statement/control_flow.rs b/src/tree/statement/control_flow.rs index 53d4edd..fded136 100644 --- a/src/tree/statement/control_flow.rs +++ b/src/tree/statement/control_flow.rs @@ -236,3 +236,328 @@ impl Node for UsingIfClauseStatement { "using if clause statement".to_string() } } + +impl std::fmt::Display for IfElseStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {}", self.r#else, self.block) + } +} + +impl std::fmt::Display for IfElseIfStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {} {}", self.elseif, self.condition, self.block) + } +} + +impl std::fmt::Display for IfStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {} {}", self.r#if, self.conditions, self.block)?; + + for elseif in &self.elseifs { + write!(f, " {}", elseif)?; + } + + if let Some(r#else) = &self.r#else { + write!(f, " {}", r#else)?; + } + + Ok(()) + } +} + +impl std::fmt::Display for UsingStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} ", self.r#using)?; + + for assignment in &self.assignments.inner { + write!(f, "{} ", assignment)?; + } + + if let Some(if_clause) = &self.if_clause { + write!(f, "{} ", if_clause)?; + } + + write!(f, "{}", self.block) + } +} + +impl std::fmt::Display for UsingAssignmentStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} = {}", self.variable, self.expression) + } +} + +impl std::fmt::Display for UsingIfClauseStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {}", self.r#if, self.condition) + } +} + +impl std::fmt::Display for IfElseBlockStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + IfElseBlockStatement::If(r#if) => write!(f, "{}", r#if), + IfElseBlockStatement::Block(block) => write!(f, "{}", block), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + use crate::tree::expression::argument::ArgumentExpression; + use crate::tree::expression::argument::ArgumentListExpression; + use crate::tree::expression::literal::Literal::Integer; + use crate::tree::expression::literal::LiteralInteger; + use crate::tree::expression::operator::ComparisonOperationExpression; + use crate::tree::expression::operator::FunctionOperationExpression; + use crate::tree::identifier::Identifier; + + #[test] + pub fn test_if_statement_display() { + let r#if = IfStatement { + comments: CommentGroup { comments: vec![] }, + r#if: Keyword::new(ByteString::from("if"), 0), + conditions: CommaSeparated { + inner: vec![Expression::ComparisonOperation( + ComparisonOperationExpression::LessThan { + comments: CommentGroup { comments: vec![] }, + left: Box::from(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::from(Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("10"), + }))), + less_than: 0, + }, + )], + commas: vec![], + }, + block: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + elseifs: vec![], + r#else: None, + }; + + assert_eq!(r#if.to_string(), "if $foo < 10 { /* ... */ }"); + + let if_elseif = IfStatement { + comments: CommentGroup { comments: vec![] }, + r#if: Keyword::new(ByteString::from("if"), 0), + conditions: CommaSeparated { + inner: vec![Expression::ComparisonOperation( + ComparisonOperationExpression::LessThan { + comments: CommentGroup { comments: vec![] }, + left: Box::from(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::from(Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("10"), + }))), + less_than: 0, + }, + )], + commas: vec![], + }, + block: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + elseifs: vec![IfElseIfStatement { + comments: CommentGroup { comments: vec![] }, + elseif: Keyword::new(ByteString::from("elseif"), 0), + condition: Expression::ComparisonOperation( + ComparisonOperationExpression::LessThan { + comments: CommentGroup { comments: vec![] }, + left: Box::from(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::from(Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("10"), + }))), + less_than: 0, + }, + ), + block: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + }], + r#else: None, + }; + + assert_eq!( + if_elseif.to_string(), + "if $foo < 10 { /* ... */ } elseif $foo < 10 { /* ... */ }" + ); + + let if_else_if = IfStatement { + comments: CommentGroup { comments: vec![] }, + r#if: Keyword::new(ByteString::from("if"), 0), + conditions: CommaSeparated { + inner: vec![Expression::ComparisonOperation( + ComparisonOperationExpression::LessThan { + comments: CommentGroup { comments: vec![] }, + left: Box::from(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::from(Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("10"), + }))), + less_than: 0, + }, + )], + commas: vec![], + }, + block: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + elseifs: vec![], + r#else: Some(IfElseStatement { + comments: CommentGroup { comments: vec![] }, + r#else: Keyword::new(ByteString::from("else"), 0), + block: IfElseBlockStatement::If(Box::from(IfStatement { + comments: CommentGroup { comments: vec![] }, + r#if: Keyword::new(ByteString::from("if"), 0), + conditions: CommaSeparated { + inner: vec![Expression::ComparisonOperation( + ComparisonOperationExpression::LessThan { + comments: CommentGroup { comments: vec![] }, + left: Box::from(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::from(Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("10"), + }))), + less_than: 0, + }, + )], + commas: vec![], + }, + block: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + elseifs: vec![], + r#else: Some(IfElseStatement { + comments: CommentGroup { comments: vec![] }, + r#else: Keyword::new(ByteString::from("else"), 0), + block: IfElseBlockStatement::Block(BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }), + }), + })), + }), + }; + + assert_eq!( + if_else_if.to_string(), + "if $foo < 10 { /* ... */ } else if $foo < 10 { /* ... */ } else { /* ... */ }" + ); + } + + #[test] + pub fn test_using_statement_display() { + let using = UsingStatement { + comments: CommentGroup { comments: vec![] }, + r#using: Keyword::new(ByteString::from("using"), 0), + assignments: CommaSeparated { + inner: vec![UsingAssignmentStatement { + comments: CommentGroup { comments: vec![] }, + variable: Variable { + position: 0, + name: ByteString::from("foo"), + }, + equals: 0, + expression: Expression::FunctionOperation(FunctionOperationExpression::Call { + comments: CommentGroup { comments: vec![] }, + function: Box::new(Expression::Identifier(Identifier { + position: 0, + value: ByteString::from("bar"), + })), + generics: None, + arguments: ArgumentListExpression { + comments: CommentGroup { comments: vec![] }, + left_parenthesis: 0, + arguments: CommaSeparated { + inner: vec![ArgumentExpression::Value { + comments: CommentGroup { comments: vec![] }, + value: Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("1"), + })), + }], + commas: vec![], + }, + right_parenthesis: 0, + }, + }), + }], + commas: vec![], + }, + block: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + if_clause: Some(UsingIfClauseStatement { + comments: CommentGroup { comments: vec![] }, + r#if: Keyword::new(ByteString::from("if"), 0), + condition: Expression::ComparisonOperation( + ComparisonOperationExpression::LessThan { + comments: CommentGroup { comments: vec![] }, + left: Box::from(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::from(Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("10"), + }))), + less_than: 0, + }, + ), + }), + }; + + assert_eq!( + using.to_string(), + "using $foo = bar(1) if $foo < 10 { /* ... */ }" + ); + } +} diff --git a/src/tree/statement/expression.rs b/src/tree/statement/expression.rs index e54cca9..d42e865 100644 --- a/src/tree/statement/expression.rs +++ b/src/tree/statement/expression.rs @@ -37,3 +37,9 @@ impl Node for ExpressionStatement { "expression statement".to_string() } } + +impl std::fmt::Display for ExpressionStatement { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{};", self.expression) + } +} diff --git a/src/tree/statement/loop.rs b/src/tree/statement/loop.rs index 9f5ba0c..c8e70df 100644 --- a/src/tree/statement/loop.rs +++ b/src/tree/statement/loop.rs @@ -467,3 +467,539 @@ impl Node for ContinueStatement { "continue statement".to_string() } } + +impl std::fmt::Display for ForeachStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {} {}", self.r#foreach, self.iterator, self.block)?; + + if let (Some(r#else), Some(else_block)) = (&self.r#else, &self.else_block) { + write!(f, " {} {}", r#else, else_block)?; + } + + Ok(()) + } +} + +impl std::fmt::Display for ForeachIteratorStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::Value { + expression, + r#as, + value, + } => { + write!(f, "{} {} {}", expression, r#as, value) + } + Self::ParenthesizedValue { + expression, + r#as, + value, + .. + } => { + write!(f, "({} {} {})", expression, r#as, value) + } + Self::KeyAndValue { + expression, + r#as, + key, + value, + .. + } => { + write!(f, "{} {} {} => {}", expression, r#as, key, value) + } + Self::ParenthesizedKeyAndValue { + expression, + r#as, + key, + value, + .. + } => { + write!(f, "({} {} {} => {})", expression, r#as, key, value) + } + } + } +} + +impl std::fmt::Display for ForIteratorStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::Standalone { + initializations, + conditions, + r#loop, + .. + } => { + write!(f, "{}; {}; {}", initializations, conditions, r#loop) + } + Self::Parenthesized { + initializations, + conditions, + r#loop, + .. + } => { + write!(f, "({}; {}; {})", initializations, conditions, r#loop) + } + } + } +} + +impl std::fmt::Display for ForStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {} {}", self.r#for, self.iterator, self.block) + } +} + +impl std::fmt::Display for WhileStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {} {}", self.r#while, self.conditions, self.block) + } +} + +impl std::fmt::Display for DoWhileStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} {} {} {};", + self.r#do, self.block, self.r#while, self.conditions + ) + } +} + +impl std::fmt::Display for BreakStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.r#break)?; + + if let Some(level) = &self.level { + write!(f, " {}", level)?; + } + + write!(f, ";") + } +} + +impl std::fmt::Display for ContinueStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.r#continue)?; + + if let Some(level) = &self.level { + write!(f, " {}", level)?; + } + + write!(f, ";") + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + use crate::tree::expression::literal::Literal::Integer; + use crate::tree::expression::operator::ArithmeticOperationExpression; + use crate::tree::expression::operator::ComparisonOperationExpression; + + #[test] + pub fn test_foreach_statement_display() { + let foreach = ForeachStatement { + r#foreach: Keyword::new(ByteString::from("foreach"), 0), + iterator: ForeachIteratorStatement::Value { + expression: Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + }), + r#as: Keyword::new(ByteString::from("as"), 0), + value: Variable { + position: 0, + name: ByteString::from("bar"), + }, + }, + block: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + r#else: None, + else_block: None, + comments: CommentGroup { comments: vec![] }, + }; + + assert_eq!(foreach.to_string(), "foreach $foo as $bar { /* ... */ }"); + + let foreach_with_else = ForeachStatement { + r#foreach: Keyword::new(ByteString::from("foreach"), 0), + iterator: ForeachIteratorStatement::Value { + expression: Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + }), + r#as: Keyword::new(ByteString::from("as"), 0), + value: Variable { + position: 0, + name: ByteString::from("bar"), + }, + }, + block: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + r#else: Some(Keyword::new(ByteString::from("else"), 0)), + else_block: Some(BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }), + comments: CommentGroup { comments: vec![] }, + }; + + assert_eq!( + foreach_with_else.to_string(), + "foreach $foo as $bar { /* ... */ } else { /* ... */ }" + ); + + let foreach_with_parenthesized_value = ForeachStatement { + r#foreach: Keyword::new(ByteString::from("foreach"), 0), + iterator: ForeachIteratorStatement::ParenthesizedValue { + expression: Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + }), + r#as: Keyword::new(ByteString::from("as"), 0), + value: Variable { + position: 0, + name: ByteString::from("bar"), + }, + left_parenthesis: 0, + right_parenthesis: 0, + }, + block: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + r#else: None, + else_block: None, + comments: CommentGroup { comments: vec![] }, + }; + + assert_eq!( + foreach_with_parenthesized_value.to_string(), + "foreach ($foo as $bar) { /* ... */ }" + ); + + let foreach_with_key_and_value = ForeachStatement { + r#foreach: Keyword::new(ByteString::from("foreach"), 0), + iterator: ForeachIteratorStatement::KeyAndValue { + expression: Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + }), + r#as: Keyword::new(ByteString::from("as"), 0), + key: Variable { + position: 0, + name: ByteString::from("bar"), + }, + double_arrow: 0, + value: Variable { + position: 0, + name: ByteString::from("baz"), + }, + }, + block: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + r#else: None, + else_block: None, + comments: CommentGroup { comments: vec![] }, + }; + + assert_eq!( + foreach_with_key_and_value.to_string(), + "foreach $foo as $bar => $baz { /* ... */ }" + ); + + let foreach_with_parenthesized_key_and_value = ForeachStatement { + r#foreach: Keyword::new(ByteString::from("foreach"), 0), + iterator: ForeachIteratorStatement::ParenthesizedKeyAndValue { + expression: Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + }), + r#as: Keyword::new(ByteString::from("as"), 0), + key: Variable { + position: 0, + name: ByteString::from("bar"), + }, + double_arrow: 0, + value: Variable { + position: 0, + name: ByteString::from("baz"), + }, + left_parenthesis: 0, + right_parenthesis: 0, + }, + block: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + r#else: None, + else_block: None, + comments: CommentGroup { comments: vec![] }, + }; + + assert_eq!( + foreach_with_parenthesized_key_and_value.to_string(), + "foreach ($foo as $bar => $baz) { /* ... */ }" + ); + } + + #[test] + pub fn test_for_statement_display() { + let standalone_for_statement = ForStatement { + r#for: Keyword::new(ByteString::from("for"), 0), + iterator: ForIteratorStatement::Standalone { + initializations: CommaSeparated { + inner: vec![Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })], + commas: vec![], + }, + initializations_semicolon: 0, + conditions: CommaSeparated { + inner: vec![Expression::ComparisonOperation( + ComparisonOperationExpression::LessThan { + comments: CommentGroup { comments: vec![] }, + left: Box::from(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::from(Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("10"), + }))), + less_than: 0, + }, + )], + commas: vec![], + }, + conditions_semicolon: 0, + r#loop: CommaSeparated { + inner: vec![Expression::ArithmeticOperation( + ArithmeticOperationExpression::PostIncrement { + left: Box::from(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + increment: 0, + }, + )], + commas: vec![], + }, + }, + block: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + comments: CommentGroup { comments: vec![] }, + }; + + assert_eq!( + standalone_for_statement.to_string(), + "for $foo; $foo < 10; $foo++ { /* ... */ }" + ); + + let for_statement_with_parentheses = ForStatement { + r#for: Keyword::new(ByteString::from("for"), 0), + iterator: ForIteratorStatement::Parenthesized { + left_parenthesis: 0, + initializations: CommaSeparated { + inner: vec![Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })], + commas: vec![], + }, + initializations_semicolon: 0, + conditions: CommaSeparated { + inner: vec![Expression::ComparisonOperation( + ComparisonOperationExpression::LessThan { + comments: CommentGroup { comments: vec![] }, + left: Box::from(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::from(Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("10"), + }))), + less_than: 0, + }, + )], + commas: vec![], + }, + conditions_semicolon: 0, + r#loop: CommaSeparated { + inner: vec![Expression::ArithmeticOperation( + ArithmeticOperationExpression::PostIncrement { + left: Box::from(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + increment: 0, + }, + )], + commas: vec![], + }, + right_parenthesis: 0, + }, + block: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + comments: CommentGroup { comments: vec![] }, + }; + + assert_eq!( + for_statement_with_parentheses.to_string(), + "for ($foo; $foo < 10; $foo++) { /* ... */ }" + ); + } + + #[test] + pub fn test_while_statement_display() { + let while_statement = WhileStatement { + r#while: Keyword::new(ByteString::from("while"), 0), + block: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + comments: CommentGroup { comments: vec![] }, + conditions: CommaSeparated { + inner: vec![Expression::ComparisonOperation( + ComparisonOperationExpression::LessThan { + comments: CommentGroup { comments: vec![] }, + left: Box::from(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::from(Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("10"), + }))), + less_than: 0, + }, + )], + commas: vec![], + }, + }; + + assert_eq!(while_statement.to_string(), "while $foo < 10 { /* ... */ }"); + } + + #[test] + pub fn test_do_while_statement_display() { + let do_while_statement = DoWhileStatement { + r#do: Keyword::new(ByteString::from("do"), 0), + block: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + comments: CommentGroup { comments: vec![] }, + r#while: Keyword::new(ByteString::from("while"), 0), + conditions: CommaSeparated { + inner: vec![Expression::ComparisonOperation( + ComparisonOperationExpression::LessThan { + comments: CommentGroup { comments: vec![] }, + left: Box::from(Expression::Variable(Variable { + position: 0, + name: ByteString::from("foo"), + })), + right: Box::from(Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("10"), + }))), + less_than: 0, + }, + )], + commas: vec![], + }, + semicolon: 0, + }; + + assert_eq!( + do_while_statement.to_string(), + "do { /* ... */ } while $foo < 10;" + ); + } + + #[test] + pub fn test_continue_statement_display() { + let continue_statement = ContinueStatement { + r#continue: Keyword::new(ByteString::from("continue"), 0), + level: None, + semicolon: 0, + comments: CommentGroup { comments: vec![] }, + }; + + assert_eq!(continue_statement.to_string(), "continue;"); + + let continue_statement_with_level = ContinueStatement { + r#continue: Keyword::new(ByteString::from("continue"), 0), + level: Some(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("2"), + }), + semicolon: 0, + comments: CommentGroup { comments: vec![] }, + }; + + assert_eq!(continue_statement_with_level.to_string(), "continue 2;"); + } + + #[test] + pub fn test_break_statement_display() { + let break_statement = BreakStatement { + r#break: Keyword::new(ByteString::from("break"), 0), + level: None, + semicolon: 0, + comments: CommentGroup { comments: vec![] }, + }; + + assert_eq!(break_statement.to_string(), "break;"); + + let break_statement_with_level = BreakStatement { + r#break: Keyword::new(ByteString::from("break"), 0), + level: Some(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("2"), + }), + semicolon: 0, + comments: CommentGroup { comments: vec![] }, + }; + + assert_eq!(break_statement_with_level.to_string(), "break 2;"); + } +} diff --git a/src/tree/statement/mod.rs b/src/tree/statement/mod.rs index e9a5a87..b066b4c 100644 --- a/src/tree/statement/mod.rs +++ b/src/tree/statement/mod.rs @@ -121,3 +121,23 @@ impl Node for Statement { } } } + +impl std::fmt::Display for Statement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::DoWhile(statement) => write!(f, "{}", statement), + Self::While(statement) => write!(f, "{}", statement), + Self::For(statement) => write!(f, "{}", statement), + Self::Foreach(statement) => write!(f, "{}", statement), + Self::Break(statement) => write!(f, "{}", statement), + Self::Continue(statement) => write!(f, "{}", statement), + Self::If(statement) => write!(f, "{}", statement), + Self::Using(statement) => write!(f, "{}", statement), + Self::Try(statement) => write!(f, "{}", statement), + Self::Expression(statement) => write!(f, "{}", statement), + Self::Return(statement) => write!(f, "{}", statement), + Self::Block(statement) => write!(f, "{}", statement), + Self::Empty(_) => write!(f, ";"), + } + } +} diff --git a/src/tree/statement/return.rs b/src/tree/statement/return.rs index 7e6465b..3c3a857 100644 --- a/src/tree/statement/return.rs +++ b/src/tree/statement/return.rs @@ -70,3 +70,57 @@ impl Node for ReturnStatement { } } } + +impl std::fmt::Display for ReturnStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::Explicit { + r#return, + expression, + .. + } => { + if let Some(expression) = expression { + write!(f, "{} {};", r#return, expression) + } else { + write!(f, "{};", r#return) + } + } + Self::Implicit { expression, .. } => write!(f, "{}", expression), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + use crate::tree::expression::literal::Literal::Integer; + use crate::tree::expression::literal::LiteralInteger; + + #[test] + pub fn test_return_statement_display() { + let explicit_return_statement = ReturnStatement::Explicit { + comments: CommentGroup { comments: vec![] }, + r#return: Keyword::new(ByteString::from("return"), 0), + expression: Some(Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("10"), + }))), + semicolon: 0, + }; + + assert_eq!(explicit_return_statement.to_string(), "return 10;"); + + let implicit_return_statement = ReturnStatement::Implicit { + comments: CommentGroup { comments: vec![] }, + expression: Expression::Literal(Integer(LiteralInteger { + comments: CommentGroup { comments: vec![] }, + position: 0, + value: ByteString::from("10"), + })), + }; + + assert_eq!(implicit_return_statement.to_string(), "10"); + } +} diff --git a/src/tree/statement/try.rs b/src/tree/statement/try.rs index 4cc33d2..74bb412 100644 --- a/src/tree/statement/try.rs +++ b/src/tree/statement/try.rs @@ -165,3 +165,171 @@ impl Node for TryCatchTypeStatement { "try catch type statement".to_string() } } + +impl std::fmt::Display for TryCatchTypeStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::Identifier(identifier) => write!(f, "{}", identifier), + Self::Union(identifiers) => { + write!( + f, + "{}", + identifiers + .iter() + .map(|identifier| identifier.to_string()) + .collect::>() + .join(" | ") + ) + } + } + } +} + +impl std::fmt::Display for TryCatchBlockStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} ({}", self.catch, self.types)?; + if let Some(variable) = &self.variable { + write!(f, " {}", variable)?; + } + write!(f, ") {}", self.block) + } +} + +impl std::fmt::Display for TryFinallyBlockStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {}", self.finally, self.block) + } +} + +impl std::fmt::Display for TryStatement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {}", self.r#try, self.block)?; + + for catch in &self.catches { + write!(f, " {}", catch)?; + } + + if let Some(finally) = &self.finally { + write!(f, " {}", finally)?; + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::byte_string::ByteString; + + #[test] + pub fn test_try_statement_display() { + let try_statement = TryStatement { + comments: CommentGroup { comments: vec![] }, + r#try: Keyword::new(ByteString::from("try"), 0), + block: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + catches: vec![ + TryCatchBlockStatement { + comments: CommentGroup { comments: vec![] }, + catch: Keyword::new(ByteString::from("catch"), 0), + left_parenthesis: 0, + types: TryCatchTypeStatement::Identifier(Identifier { + position: 0, + value: ByteString::from("Exception"), + }), + variable: Some(Variable { + position: 0, + name: ByteString::from("e"), + }), + right_parenthesis: 0, + block: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + }, + TryCatchBlockStatement { + comments: CommentGroup { comments: vec![] }, + catch: Keyword::new(ByteString::from("catch"), 0), + left_parenthesis: 0, + types: TryCatchTypeStatement::Identifier(Identifier { + position: 0, + value: ByteString::from("CustomException"), + }), + variable: Some(Variable { + position: 0, + name: ByteString::from("e"), + }), + right_parenthesis: 0, + block: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + }, + ], + finally: Some(TryFinallyBlockStatement { + comments: CommentGroup { comments: vec![] }, + finally: Keyword::new(ByteString::from("finally"), 0), + block: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + }), + }; + + assert_eq!(try_statement.to_string(), "try { /* ... */ } catch (Exception $e) { /* ... */ } catch (CustomException $e) { /* ... */ } finally { /* ... */ }"); + + let try_statement_with_union_catch = TryStatement { + comments: CommentGroup { comments: vec![] }, + r#try: Keyword::new(ByteString::from("try"), 0), + block: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + catches: vec![TryCatchBlockStatement { + comments: CommentGroup { comments: vec![] }, + catch: Keyword::new(ByteString::from("catch"), 0), + left_parenthesis: 0, + types: TryCatchTypeStatement::Union(vec![ + Identifier { + position: 0, + value: ByteString::from("Exception"), + }, + Identifier { + position: 0, + value: ByteString::from("CustomException"), + }, + ]), + variable: Some(Variable { + position: 0, + name: ByteString::from("e"), + }), + right_parenthesis: 0, + block: BlockStatement { + comments: CommentGroup { comments: vec![] }, + left_brace: 0, + statements: vec![], + right_brace: 0, + }, + }], + finally: None, + }; + + assert_eq!( + try_statement_with_union_catch.to_string(), + "try { /* ... */ } catch (Exception | CustomException $e) { /* ... */ }" + ); + } +} diff --git a/src/tree/token.rs b/src/tree/token.rs index 9e841e3..d3a29c1 100644 --- a/src/tree/token.rs +++ b/src/tree/token.rs @@ -50,3 +50,15 @@ impl Display for Keyword { write!(f, "{}", self.value) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_keyword_display() { + let keyword = Keyword::new(ByteString::from("iterable"), 0); + + assert_eq!(keyword.to_string(), "iterable"); + } +} diff --git a/src/tree/utils.rs b/src/tree/utils.rs index ae69773..2bb6bfa 100644 --- a/src/tree/utils.rs +++ b/src/tree/utils.rs @@ -12,3 +12,17 @@ pub struct CommaSeparated { pub inner: Vec, pub commas: Vec, // `,` } + +impl std::fmt::Display for CommaSeparated { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + f, + "{}", + self.inner + .iter() + .map(|node| node.to_string()) + .collect::>() + .join(", ") + ) + } +} diff --git a/src/tree/variable.rs b/src/tree/variable.rs index 9952744..2c9ac14 100644 --- a/src/tree/variable.rs +++ b/src/tree/variable.rs @@ -16,12 +16,6 @@ pub struct Variable { pub name: ByteString, } -impl Display for Variable { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.name) - } -} - impl Node for Variable { fn initial_position(&self) -> usize { self.position @@ -39,3 +33,24 @@ impl Node for Variable { "variable".to_string() } } + +impl Display for Variable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "${}", self.name) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_variable_display() { + let variable = Variable { + position: 0, + name: ByteString::from("foo"), + }; + + assert_eq!(variable.to_string(), "$foo"); + } +}