Skip to content

Commit

Permalink
Add support for leading | and & in types (#306)
Browse files Browse the repository at this point in the history
This PR changes `TypeInfo::Union` and `TypeInfo::Intersection` to both
use a separate struct: `TypeUnion` and `TypeIntersection`. It also
changes the structure of these types from being linked lists into `Vec`s
(using `Punctuated`). This also results in some new visitor methods:
`visit_type_union`, `visit_type_union_end`, `visit_type_intersection`,
and `visit_type_intersection_end`.

This PR also adds the ability to parse leading `|` and `&` in types,
such as:
```luau
type T = | "A" | "B" | "C"
```
This involved adding a `leading` field to the `TypeUnion` and
`TypeIntersection` structs, which contains an optional `TokenReference`.

Tests were updated to accommodate the changes, and leading `|` and `&`
was added to the `types` test.

---------

Co-authored-by: Chris Chang <[email protected]>
Co-authored-by: boyned//Kampfkarren <[email protected]>
  • Loading branch information
3 people authored Jul 27, 2024
1 parent ef8564e commit 4bb43f2
Show file tree
Hide file tree
Showing 13 changed files with 5,977 additions and 4,951 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ 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
- 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
- **[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
98 changes: 78 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,78 @@ 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: Option<TokenReference>) -> Self {
Self { leading, ..self }
}

/// 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 {
/// 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 ampersand.
pub fn with_leading(self, leading: Option<TokenReference>) -> Self {
Self { leading, ..self }
}

/// The leading ampersand, if one is present: `&`.
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

0 comments on commit 4bb43f2

Please sign in to comment.