Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CHANGELOG.d/feature_leading-and-trailing-delimiters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
* Allow leading and trailing delimiters in many syntax positions

PureScript's syntax contains multiple places where sequences of words or
clauses are expected, delimited by commas or vertical bars. To
facilitate editing these elements when placed on their own lines,
PureScript's parser now supports an optional leading or trailing
delimiter in most such locations, specifically:

* Array and object literals (and patterns)
* Sum type definitions
* Object updates
* Row types
* Import and export lists
* Functional dependencies
* Compound constraints and superclasses
12 changes: 6 additions & 6 deletions src/Language/PureScript/CST/Convert.hs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ convertType fileName = go
let ann = sourceAnnCommented fileName (lblTok a) (snd $ typeRange ty)
T.RCons ann (L.Label $ lblName a) (go ty) c
case labels of
Just (Separated h t) ->
Just (SeparatedExtra _ h t _) ->
rowCons h $ foldr (rowCons . snd) rowTail t
Nothing ->
rowTail
Expand Down Expand Up @@ -276,7 +276,7 @@ convertExpr fileName = go
let
ann = sourceAnnCommented fileName a c
vals = case bs of
Just (Separated x xs) -> go x : (go . snd <$> xs)
Just (SeparatedExtra _ x xs _) -> go x : (go . snd <$> xs)
Nothing -> []
positioned ann . AST.Literal (fst ann) $ AST.ArrayLiteral vals
ExprRecord z (Wrapped a bs c) -> do
Expand All @@ -286,7 +286,7 @@ convertExpr fileName = go
RecordPun f -> (mkString . getIdent $ nameValue f, go . ExprIdent z $ QualifiedName (nameTok f) Nothing (nameValue f))
RecordField f _ v -> (lblName f, go v)
vals = case bs of
Just (Separated x xs) -> lbl x : (lbl . snd <$> xs)
Just (SeparatedExtra _ x xs _) -> lbl x : (lbl . snd <$> xs)
Nothing -> []
positioned ann . AST.Literal (fst ann) $ AST.ObjectLiteral vals
ExprParens _ (Wrapped a b c) ->
Expand Down Expand Up @@ -399,7 +399,7 @@ convertBinder fileName = go
let
ann = sourceAnnCommented fileName a c
vals = case bs of
Just (Separated x xs) -> go x : (go . snd <$> xs)
Just (SeparatedExtra _ x xs _) -> go x : (go . snd <$> xs)
Nothing -> []
positioned ann . AST.LiteralBinder (fst ann) $ AST.ArrayLiteral vals
BinderRecord z (Wrapped a bs c) -> do
Expand All @@ -409,7 +409,7 @@ convertBinder fileName = go
RecordPun f -> (mkString . getIdent $ nameValue f, go $ BinderVar z f)
RecordField f _ v -> (lblName f, go v)
vals = case bs of
Just (Separated x xs) -> lbl x : (lbl . snd <$> xs)
Just (SeparatedExtra _ x xs _) -> lbl x : (lbl . snd <$> xs)
Nothing -> []
positioned ann . AST.LiteralBinder (fst ann) $ AST.ObjectLiteral vals
BinderParens _ (Wrapped a b c) ->
Expand Down Expand Up @@ -442,7 +442,7 @@ convertDeclaration fileName decl = case decl of
[] -> []
(st', ctor) : tl' -> ctrs st' ctor tl'
)
pure $ AST.DataDeclaration ann Env.Data (nameValue a) (goTypeVar <$> vars) (maybe [] (\(st, Separated hd tl) -> ctrs st hd tl) bd)
pure $ AST.DataDeclaration ann Env.Data (nameValue a) (goTypeVar <$> vars) (maybe [] (\(st, SeparatedExtra _ hd tl _) -> ctrs st hd tl) bd)
DeclType _ (DataHead _ a vars) _ bd ->
pure $ AST.TypeSynonymDeclaration ann
(nameValue a)
Expand Down
29 changes: 16 additions & 13 deletions src/Language/PureScript/CST/Flatten.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ flattenModule :: Module a -> DList SourceToken
flattenModule m@(Module _ a b c d e f g) =
pure a <>
flattenName b <>
foldMap (flattenWrapped (flattenSeparated flattenExport)) c <>
foldMap (flattenWrapped (flattenSeparatedExtra flattenExport)) c <>
pure d <>
foldMap flattenImportDecl e <>
foldMap flattenDeclaration f <>
Expand All @@ -32,7 +32,7 @@ flattenClassHead (ClassHead a b c d e) =
foldMap (\(f, g) -> flattenOneOrDelimited flattenConstraint f <> pure g) b <>
flattenName c <>
foldMap flattenTypeVarBinding d <>
foldMap (\(f, g) -> pure f <> flattenSeparated flattenClassFundep g) e
foldMap (\(f, g) -> pure f <> flattenSeparatedExtra flattenClassFundep g) e

flattenClassFundep :: ClassFundep -> DList SourceToken
flattenClassFundep = \case
Expand Down Expand Up @@ -74,9 +74,9 @@ flattenBinder = \case
BinderChar _ a _ -> pure a
BinderString _ a _ -> pure a
BinderNumber _ a b _ -> foldMap pure a <> pure b
BinderArray _ a -> flattenWrapped (foldMap (flattenSeparated flattenBinder)) a
BinderArray _ a -> flattenWrapped (foldMap (flattenSeparatedExtra flattenBinder)) a
BinderRecord _ a ->
flattenWrapped (foldMap (flattenSeparated (flattenRecordLabeled flattenBinder))) a
flattenWrapped (foldMap (flattenSeparatedExtra (flattenRecordLabeled flattenBinder))) a
BinderParens _ a -> flattenWrapped flattenBinder a
BinderTyped _ a b c -> flattenBinder a <> pure b <> flattenType c
BinderOp _ a b c -> flattenBinder a <> flattenQualifiedName b <> flattenBinder c
Expand All @@ -94,7 +94,7 @@ flattenRecordUpdate :: RecordUpdate a -> DList SourceToken
flattenRecordUpdate = \case
RecordUpdateLeaf a b c -> flattenLabel a <> pure b <> flattenExpr c
RecordUpdateBranch a b ->
flattenLabel a <> flattenWrapped (flattenSeparated flattenRecordUpdate) b
flattenLabel a <> flattenWrapped (flattenSeparatedExtra flattenRecordUpdate) b

flattenLambda :: Lambda a -> DList SourceToken
flattenLambda (Lambda a b c d) =
Expand Down Expand Up @@ -139,17 +139,17 @@ flattenExpr = \case
ExprChar _ a _ -> pure a
ExprString _ a _ -> pure a
ExprNumber _ a _ -> pure a
ExprArray _ a -> flattenWrapped (foldMap (flattenSeparated flattenExpr)) a
ExprArray _ a -> flattenWrapped (foldMap (flattenSeparatedExtra flattenExpr)) a
ExprRecord _ a ->
flattenWrapped (foldMap (flattenSeparated (flattenRecordLabeled flattenExpr))) a
flattenWrapped (foldMap (flattenSeparatedExtra (flattenRecordLabeled flattenExpr))) a
ExprParens _ a -> flattenWrapped flattenExpr a
ExprTyped _ a b c -> flattenExpr a <> pure b <> flattenType c
ExprInfix _ a b c -> flattenExpr a <> flattenWrapped flattenExpr b <> flattenExpr c
ExprOp _ a b c -> flattenExpr a <> flattenQualifiedName b <> flattenExpr c
ExprOpName _ a -> flattenQualifiedName a
ExprNegate _ a b -> pure a <> flattenExpr b
ExprRecordAccessor _ a -> flattenRecordAccessor a
ExprRecordUpdate _ a b -> flattenExpr a <> flattenWrapped (flattenSeparated flattenRecordUpdate) b
ExprRecordUpdate _ a b -> flattenExpr a <> flattenWrapped (flattenSeparatedExtra flattenRecordUpdate) b
ExprApp _ a b -> flattenExpr a <> flattenExpr b
ExprLambda _ a -> flattenLambda a
ExprIf _ a -> flattenIfThenElse a
Expand Down Expand Up @@ -206,7 +206,7 @@ flattenDeclaration :: Declaration a -> DList SourceToken
flattenDeclaration = \case
DeclData _ a b ->
flattenDataHead a <>
foldMap (\(t, cs) -> pure t <> flattenSeparated flattenDataCtor cs) b
foldMap (\(t, cs) -> pure t <> flattenSeparatedExtra flattenDataCtor cs) b
DeclType _ a b c ->flattenDataHead a <> pure b <> flattenType c
DeclNewtype _ a b c d -> flattenDataHead a <> pure b <> flattenName c <> flattenType d
DeclClass _ a b ->
Expand Down Expand Up @@ -242,14 +242,14 @@ flattenExport = \case
flattenDataMembers :: DataMembers a -> DList SourceToken
flattenDataMembers = \case
DataAll _ t -> pure t
DataEnumerated _ ns -> flattenWrapped (foldMap (flattenSeparated flattenName)) ns
DataEnumerated _ ns -> flattenWrapped (foldMap (flattenSeparatedExtra flattenName)) ns

flattenImportDecl :: ImportDecl a -> DList SourceToken
flattenImportDecl (ImportDecl _ a b c d) =
pure a <>
flattenName b <>
foldMap (\(mt, is) ->
foldMap pure mt <> flattenWrapped (flattenSeparated flattenImport) is) c <>
foldMap pure mt <> flattenWrapped (flattenSeparatedExtra flattenImport) is) c <>
foldMap (\(t, n) -> pure t <> flattenName n) d

flattenImport :: Import a -> DList SourceToken
Expand All @@ -266,11 +266,14 @@ flattenWrapped k (Wrapped a b c) = pure a <> k b <> pure c
flattenSeparated :: (a -> DList SourceToken) -> Separated a -> DList SourceToken
flattenSeparated k (Separated a b) = k a <> foldMap (\(c, d) -> pure c <> k d) b

flattenSeparatedExtra :: (a -> DList SourceToken) -> SeparatedExtra a -> DList SourceToken
flattenSeparatedExtra k (SeparatedExtra a b c d) = foldMap pure a <> k b <> foldMap (\(e, f) -> pure e <> k f) c <> foldMap pure d

flattenOneOrDelimited
:: (a -> DList SourceToken) -> OneOrDelimited a -> DList SourceToken
flattenOneOrDelimited f = \case
One a -> f a
Many a -> flattenWrapped (flattenSeparated f) a
Many a -> flattenWrapped (flattenSeparatedExtra f) a

flattenLabeled :: (a -> DList SourceToken) -> (b -> DList SourceToken) -> Labeled a b -> DList SourceToken
flattenLabeled ka kc (Labeled a b c) = ka a <> pure b <> kc c
Expand Down Expand Up @@ -298,7 +301,7 @@ flattenType = \case

flattenRow :: Row a -> DList SourceToken
flattenRow (Row lbls tl) =
foldMap (flattenSeparated (flattenLabeled (pure . lblTok) flattenType)) lbls
foldMap (flattenSeparatedExtra (flattenLabeled (pure . lblTok) flattenType)) lbls
<> foldMap (\(a, b) -> pure a <> flattenType b) tl

flattenTypeVarBinding :: TypeVarBinding a -> DList SourceToken
Expand Down
35 changes: 20 additions & 15 deletions src/Language/PureScript/CST/Parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,18 @@ manyOrEmpty(a) :: { [a] }
sep(a, s) :: { Separated a }
: sep1(a, s) { separated $1 }

sepx(a, s) :: { SeparatedExtra a }
: sep1(a, s) { separatedExtra Nothing Nothing $1 }
| sep1(a, s) s { separatedExtra Nothing (Just $2) $1 }
| s sep1(a, s) { separatedExtra (Just $1) Nothing $2 }

sep1(a, s) :: { [(SourceToken, a)] }
: a %shift { [(placeholder, $1)] }
| sep1(a, s) s a { ($2, $3) : $1 }

delim(a, b, c, d) :: { Delimited b }
: a d { Wrapped $1 Nothing $2 }
| a sep(b, c) d { Wrapped $1 (Just $2) $3 }
| a sepx(b, c) d { Wrapped $1 (Just $2) $3 }

moduleName :: { Name N.ModuleName }
: UPPER {% upperToModuleName $1 }
Expand Down Expand Up @@ -340,8 +345,8 @@ typeKindedAtom :: { Type () }
row :: { Row () }
: {- empty -} { Row Nothing Nothing }
| '|' type { Row Nothing (Just ($1, $2)) }
| sep(rowLabel, ',') { Row (Just $1) Nothing }
| sep(rowLabel, ',') '|' type { Row (Just $1) (Just ($2, $3)) }
| sepx(rowLabel, ',') { Row (Just $1) Nothing }
| sepx(rowLabel, ',') '|' type { Row (Just $1) (Just ($2, $3)) }

rowLabel :: { Labeled Label (Type ()) }
: label '::' type { Labeled $1 $2 $3 }
Expand Down Expand Up @@ -409,7 +414,7 @@ expr5 :: { Expr () }
expr6 :: { Expr () }
: expr7 %shift { $1 }
| expr7 '{' '}' { ExprApp () $1 (ExprRecord () (Wrapped $2 Nothing $3)) }
| expr7 '{' sep(recordUpdateOrLabel, ',') '}'
| expr7 '{' sepx(recordUpdateOrLabel, ',') '}'
{% toRecordFields $3 >>= \case
Left xs -> pure $ ExprApp () $1 (ExprRecord () (Wrapped $2 (Just xs) $4))
Right xs -> pure $ ExprRecordUpdate () $1 (Wrapped $2 xs $4)
Expand Down Expand Up @@ -442,11 +447,11 @@ recordUpdateOrLabel :: { Either (RecordLabeled (Expr ())) (RecordUpdate ()) }
: label ':' expr { Left (RecordField $1 $2 $3) }
| label {% fmap (Left . RecordPun) . toName Ident $ lblTok $1 }
| label '=' expr { Right (RecordUpdateLeaf $1 $2 $3) }
| label '{' sep(recordUpdate, ',') '}' { Right (RecordUpdateBranch $1 (Wrapped $2 $3 $4)) }
| label '{' sepx(recordUpdate, ',') '}' { Right (RecordUpdateBranch $1 (Wrapped $2 $3 $4)) }

recordUpdate :: { RecordUpdate () }
: label '=' expr { RecordUpdateLeaf $1 $2 $3 }
| label '{' sep(recordUpdate, ',') '}' { RecordUpdateBranch $1 (Wrapped $2 $3 $4) }
| label '{' sepx(recordUpdate, ',') '}' { RecordUpdateBranch $1 (Wrapped $2 $3 $4) }

letBinding :: { LetBinding () }
: ident '::' type { LetBindingSignature () (Labeled $1 $2 $3) }
Expand Down Expand Up @@ -620,7 +625,7 @@ declElse :: { SourceToken }

exports :: { Maybe (DelimitedNonEmpty (Export ())) }
: {- empty -} { Nothing }
| '(' sep(export, ',') ')' { Just (Wrapped $1 $2 $3) }
| '(' sepx(export, ',') ')' { Just (Wrapped $1 $2 $3) }

export :: { Export () }
: ident { ExportValue () $1 }
Expand All @@ -634,16 +639,16 @@ export :: { Export () }
dataMembers :: { (DataMembers ()) }
: '(..)' { DataAll () $1 }
| '(' ')' { DataEnumerated () (Wrapped $1 Nothing $2) }
| '(' sep(properName, ',') ')' { DataEnumerated () (Wrapped $1 (Just \$ getProperName <\$> $2) $3) }
| '(' sepx(properName, ',') ')' { DataEnumerated () (Wrapped $1 (Just \$ getProperName <\$> $2) $3) }

importDecl :: { ImportDecl () }
: 'import' moduleName imports { ImportDecl () $1 $2 $3 Nothing }
| 'import' moduleName imports 'as' moduleName { ImportDecl () $1 $2 $3 (Just ($4, $5)) }

imports :: { Maybe (Maybe SourceToken, DelimitedNonEmpty (Import ())) }
: {- empty -} { Nothing }
| '(' sep(import, ',') ')' { Just (Nothing, Wrapped $1 $2 $3) }
| 'hiding' '(' sep(import, ',') ')' { Just (Just $1, Wrapped $2 $3 $4) }
| '(' sepx(import, ',') ')' { Just (Nothing, Wrapped $1 $2 $3) }
| 'hiding' '(' sepx(import, ',') ')' { Just (Just $1, Wrapped $2 $3 $4) }

import :: { Import () }
: ident { ImportValue () $1 }
Expand All @@ -655,7 +660,7 @@ import :: { Import () }

decl :: { Declaration () }
: dataHead { DeclData () $1 Nothing }
| dataHead '=' sep(dataCtor, '|') { DeclData () $1 (Just ($2, $3)) }
| dataHead '=' sepx(dataCtor, '|') { DeclData () $1 (Just ($2, $3)) }
| typeHead '=' type {% checkNoWildcards $3 *> pure (DeclType () $1 $2 $3) }
| newtypeHead '=' properName typeAtom {% checkNoWildcards $4 *> pure (DeclNewtype () $1 $2 (getProperName $3) $4) }
| classHead { either id (\h -> DeclClass () h Nothing) $1 }
Expand Down Expand Up @@ -714,12 +719,12 @@ classSignature :: { Labeled (Name (N.ProperName 'N.TypeName)) (Type ()) }
classSuper :: { (OneOrDelimited (Constraint ()), SourceToken) }
: constraints '<=' {%^ revert $ pure ($1, $2) }

classNameAndFundeps :: { (Name (N.ProperName 'N.ClassName), [TypeVarBinding ()], Maybe (SourceToken, Separated ClassFundep)) }
classNameAndFundeps :: { (Name (N.ProperName 'N.ClassName), [TypeVarBinding ()], Maybe (SourceToken, SeparatedExtra ClassFundep)) }
: properName manyOrEmpty(typeVarBinding) fundeps {%^ revert $ pure (getProperName $1, $2, $3) }

fundeps :: { Maybe (SourceToken, Separated ClassFundep) }
fundeps :: { Maybe (SourceToken, SeparatedExtra ClassFundep) }
: {- empty -} { Nothing }
| '|' sep(fundep, ',') { Just ($1, $2) }
| '|' sepx(fundep, ',') { Just ($1, $2) }

fundep :: { ClassFundep }
: '->' many(ident) { FundepDetermined $1 $2 }
Expand All @@ -740,7 +745,7 @@ instHead :: { InstanceHead () }

constraints :: { OneOrDelimited (Constraint ()) }
: constraint { One $1 }
| '(' sep(constraint, ',') ')' { Many (Wrapped $1 $2 $3) }
| '(' sepx(constraint, ',') ')' { Many (Wrapped $1 $2 $3) }

constraint :: { Constraint () }
: qualProperName manyOrEmpty(typeAtom) {% for_ $2 checkNoWildcards *> for_ $2 checkNoForalls *> pure (Constraint () (getQualifiedProperName $1) $2) }
Expand Down
8 changes: 6 additions & 2 deletions src/Language/PureScript/CST/Positions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ sepLast :: Separated a -> a
sepLast (Separated hd []) = hd
sepLast (Separated _ tl) = snd $ last tl

sepXLast :: SeparatedExtra a -> a
sepXLast (SeparatedExtra _ hd [] _) = hd
sepXLast (SeparatedExtra _ _ tl _) = snd $ last tl

type TokenRange = (SourceToken, SourceToken)

toSourceRange :: TokenRange -> SourceRange
Expand Down Expand Up @@ -160,7 +164,7 @@ dataMembersRange = \case
declRange :: Declaration a -> TokenRange
declRange = \case
DeclData _ hd ctors
| Just (_, cs) <- ctors -> (fst start, snd . dataCtorRange $ sepLast cs)
| Just (_, cs) <- ctors -> (fst start, fromMaybe (snd . dataCtorRange $ sepXLast cs) $ sepXTrailing cs)
| otherwise -> start
where start = dataHeadRange hd
DeclType _ a _ b -> (fst $ dataHeadRange a, snd $ typeRange b)
Expand Down Expand Up @@ -191,7 +195,7 @@ dataCtorRange (DataCtor _ name fields)

classHeadRange :: ClassHead a -> TokenRange
classHeadRange (ClassHead kw _ name vars fdeps)
| Just (_, fs) <- fdeps = (kw, snd .classFundepRange $ sepLast fs)
| Just (_, fs) <- fdeps = (kw, fromMaybe (snd . classFundepRange $ sepXLast fs) $ sepXTrailing fs)
| [] <- vars = (kw, snd $ nameRange name)
| otherwise = (kw, snd . typeVarBindingRange $ last vars)

Expand Down
4 changes: 2 additions & 2 deletions src/Language/PureScript/CST/Traversals.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import Prelude

import Language.PureScript.CST.Types

everythingOnSeparated :: (r -> r -> r) -> (a -> r) -> Separated a -> r
everythingOnSeparated op k (Separated hd tl) = go hd tl
everythingOnSeparated :: (r -> r -> r) -> (a -> r) -> SeparatedExtra a -> r
everythingOnSeparated op k (SeparatedExtra _ hd tl _) = go hd tl
where
go a [] = k a
go a (b : bs) = k a `op` go (snd b) bs
17 changes: 12 additions & 5 deletions src/Language/PureScript/CST/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,21 @@ data Separated a = Separated
, sepTail :: [(SourceToken, a)]
} deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic)

data SeparatedExtra a = SeparatedExtra
{ sepXLeading :: Maybe SourceToken
, sepXHead :: a
, sepXTail :: [(SourceToken, a)]
, sepXTrailing :: Maybe SourceToken
} deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic)

data Labeled a b = Labeled
{ lblLabel :: a
, lblSep :: SourceToken
, lblValue :: b
} deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic)

type Delimited a = Wrapped (Maybe (Separated a))
type DelimitedNonEmpty a = Wrapped (Separated a)
type Delimited a = Wrapped (Maybe (SeparatedExtra a))
type DelimitedNonEmpty a = Wrapped (SeparatedExtra a)

data OneOrDelimited a
= One a
Expand Down Expand Up @@ -163,7 +170,7 @@ data Constraint a
deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic)

data Row a = Row
{ rowLabels :: Maybe (Separated (Labeled Label (Type a)))
{ rowLabels :: Maybe (SeparatedExtra (Labeled Label (Type a)))
, rowTail :: Maybe (SourceToken, Type a)
} deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic)

Expand Down Expand Up @@ -193,7 +200,7 @@ data DataMembers a
deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic)

data Declaration a
= DeclData a (DataHead a) (Maybe (SourceToken, Separated (DataCtor a)))
= DeclData a (DataHead a) (Maybe (SourceToken, SeparatedExtra (DataCtor a)))
| DeclType a (DataHead a) SourceToken (Type a)
| DeclNewtype a (DataHead a) SourceToken (Name (N.ProperName 'N.ConstructorName)) (Type a)
| DeclClass a (ClassHead a) (Maybe (SourceToken, NonEmpty (Labeled (Name Ident) (Type a))))
Expand Down Expand Up @@ -250,7 +257,7 @@ data ClassHead a = ClassHead
, clsSuper :: Maybe (OneOrDelimited (Constraint a), SourceToken)
, clsName :: Name (N.ProperName 'N.ClassName)
, clsVars :: [TypeVarBinding a]
, clsFundeps :: Maybe (SourceToken, Separated ClassFundep)
, clsFundeps :: Maybe (SourceToken, SeparatedExtra ClassFundep)
} deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic)

data ClassFundep
Expand Down
Loading