Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for leading | and & in types #306

Merged
merged 14 commits into from
Jul 27, 2024
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased
### Added
jackdotink marked this conversation as resolved.
Show resolved Hide resolved
- Added structs `TypeUnion` and `TypeIntersection` which both contain a field for a leading `TokenReference` (`|` or `&`), and a field which contains a `Punctuated<TypeInfo>`.
- Added support for parsing leading `|` and `&` in types.
### Changed
Kampfkarren marked this conversation as resolved.
Show resolved Hide resolved
- **[BREAKING CHANGE]** Changed `TypeInfo::Union` and `TypeInfo::Intersection` to hold structs `TypeUnion` and `TypeIntersection` respectively.


## [1.0.0-rc.5] - 2024-07-06
### Changed
- **[BREAKING CHANGE]** The `types` module is now named `luau`.
Expand Down
120 changes: 100 additions & 20 deletions full-moon/src/ast/luau.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,9 @@ pub enum TypeInfo {
ellipsis: TokenReference,
},

/// An intersection type: `string & number`, denoting both types.
#[display(fmt = "{left}{ampersand}{right}")]
Intersection {
/// The left hand side: `string`.
left: Box<TypeInfo>,
/// The ampersand (`&`) to separate the types.
ampersand: TokenReference,
/// The right hand side: `number`.
right: Box<TypeInfo>,
},
/// An intersection type, such as `string & number`.
#[display(fmt = "{_0}")]
Intersection(TypeIntersection),

/// A type coming from a module, such as `module.Foo`
#[display(fmt = "{module}{punctuation}{type_info}")]
Expand Down Expand Up @@ -153,16 +146,9 @@ pub enum TypeInfo {
types: Punctuated<TypeInfo>,
},

/// A union type: `string | number`, denoting one or the other.
#[display(fmt = "{left}{pipe}{right}")]
Union {
/// The left hand side: `string`.
left: Box<TypeInfo>,
/// The pipe (`|`) to separate the types.
pipe: TokenReference,
/// The right hand side: `number`.
right: Box<TypeInfo>,
},
/// A union type, such as `string | number`.
#[display(fmt = "{_0}")]
Union(TypeUnion),

/// A variadic type: `...number`.
#[display(fmt = "{ellipsis}{type_info}")]
Expand All @@ -183,6 +169,100 @@ pub enum TypeInfo {
},
}

/// A union type, such as `string | number`.
#[derive(Clone, Debug, Display, PartialEq, Node)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(fmt = "{}{types}", "display_option(leading)")]
pub struct TypeUnion {
pub(crate) leading: Option<TokenReference>,
pub(crate) types: Punctuated<TypeInfo>,
}

impl TypeUnion {
/// Creates a new Union from the given types and optional leading pipe.
pub fn new(leading: Option<TokenReference>, types: Punctuated<TypeInfo>) -> Self {
Self { leading, types }
}

/// Returns a new Union with the given types.
pub fn with_types(self, types: Punctuated<TypeInfo>) -> Self {
Self { types, ..self }
}

/// Returns a new Union with the given leading pipe.
pub fn with_leading(self, leading: TokenReference) -> Self {
Self {
leading: Some(leading),
..self
}
}

/// Returns a new Union without a leading pipe.
pub fn without_leading(self) -> Self {
Self {
leading: None,
..self
}
}
jackdotink marked this conversation as resolved.
Show resolved Hide resolved

/// The leading pipe, if one is present: `|`.
pub fn leading(&self) -> Option<&TokenReference> {
self.leading.as_ref()
}

/// The types being unioned: `string | number`.
pub fn types(&self) -> &Punctuated<TypeInfo> {
&self.types
}
}

/// An intersection type, such as `string & number`.
#[derive(Clone, Debug, Display, PartialEq, Node)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display(fmt = "{}{types}", "display_option(leading)")]
pub struct TypeIntersection {
pub(crate) leading: Option<TokenReference>,
pub(crate) types: Punctuated<TypeInfo>,
}

impl TypeIntersection {
jackdotink marked this conversation as resolved.
Show resolved Hide resolved
/// Creates a new Intersection from the given types.
pub fn new(leading: Option<TokenReference>, types: Punctuated<TypeInfo>) -> Self {
Self { leading, types }
}

/// Returns a new Intersection with the given types.
pub fn with_types(self, types: Punctuated<TypeInfo>) -> Self {
Self { types, ..self }
}

/// Returns a new Intersection with the given leading pipe.
Kampfkarren marked this conversation as resolved.
Show resolved Hide resolved
pub fn with_leading(self, leading: TokenReference) -> Self {
Self {
leading: Some(leading),
..self
}
}

/// Returns a new Intersection without a leading pipe.
pub fn without_leading(self) -> Self {
Self {
leading: None,
..self
}
}

/// The leading pipe, if one is present: `&`.
Kampfkarren marked this conversation as resolved.
Show resolved Hide resolved
pub fn leading(&self) -> Option<&TokenReference> {
self.leading.as_ref()
}

/// The types being intersected: `string & number`.
pub fn types(&self) -> &Punctuated<TypeInfo> {
&self.types
}
}

/// A subset of TypeInfo that consists of items which can only be used as an index, such as `Foo` and `Foo<Bar>`,
#[derive(Clone, Debug, Display, PartialEq, Node)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
Expand Down
78 changes: 50 additions & 28 deletions full-moon/src/ast/luau_visitors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,20 +90,8 @@ impl Visit for TypeInfo {
types.visit(visitor);
parentheses.tokens.1.visit(visitor);
}
TypeInfo::Union { left, pipe, right } => {
left.visit(visitor);
pipe.visit(visitor);
right.visit(visitor);
}
TypeInfo::Intersection {
left,
ampersand,
right,
} => {
left.visit(visitor);
ampersand.visit(visitor);
right.visit(visitor);
}
TypeInfo::Union(union) => union.visit(visitor),
TypeInfo::Intersection(intersection) => intersection.visit(visitor),
TypeInfo::Variadic {
ellipsis,
type_info,
Expand Down Expand Up @@ -250,21 +238,11 @@ impl VisitMut for TypeInfo {
TypeInfo::Tuple { parentheses, types }
}

TypeInfo::Union { left, pipe, right } => TypeInfo::Union {
left: left.visit_mut(visitor),
pipe: pipe.visit_mut(visitor),
right: right.visit_mut(visitor),
},
TypeInfo::Union(union) => TypeInfo::Union(union.visit_mut(visitor)),

TypeInfo::Intersection {
left,
ampersand,
right,
} => TypeInfo::Intersection {
left: left.visit_mut(visitor),
ampersand: ampersand.visit_mut(visitor),
right: right.visit_mut(visitor),
},
TypeInfo::Intersection(intersection) => {
TypeInfo::Intersection(intersection.visit_mut(visitor))
}

TypeInfo::Variadic {
ellipsis,
Expand All @@ -284,6 +262,50 @@ impl VisitMut for TypeInfo {
}
}

impl Visit for TypeUnion {
fn visit<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_type_union(self);

self.leading.visit(visitor);
self.types.visit(visitor);

visitor.visit_type_union_end(self);
}
}

impl VisitMut for TypeUnion {
fn visit_mut<V: VisitorMut>(mut self, visitor: &mut V) -> Self {
self = visitor.visit_type_union(self);

self.leading = self.leading.visit_mut(visitor);
self.types = self.types.visit_mut(visitor);

visitor.visit_type_union_end(self)
}
}

impl Visit for TypeIntersection {
fn visit<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_type_intersection(self);

self.leading.visit(visitor);
self.types.visit(visitor);

visitor.visit_type_intersection_end(self);
}
}

impl VisitMut for TypeIntersection {
fn visit_mut<V: VisitorMut>(mut self, visitor: &mut V) -> Self {
self = visitor.visit_type_intersection(self);

self.leading = self.leading.visit_mut(visitor);
self.types = self.types.visit_mut(visitor);

visitor.visit_type_intersection_end(self)
}
}

impl Visit for IndexedTypeInfo {
fn visit<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_indexed_type_info(self);
Expand Down
Loading