purescript-0.13.2: src/Language/PureScript/CST/Parser.y
{
module Language.PureScript.CST.Parser
( parseType
, parseKind
, parseExpr
, parseDecl
, parseIdent
, parseOperator
, parseModule
, parseImportDeclP
, parseDeclP
, parseExprP
, parseTypeP
, parseModuleNameP
, parseQualIdentP
, parse
, PartialResult(..)
) where
import Prelude hiding (lex)
import Control.Monad ((<=<), when)
import Data.Foldable (foldl', for_)
import qualified Data.List.NonEmpty as NE
import Data.Text (Text)
import Data.Traversable (for)
import Language.PureScript.CST.Errors
import Language.PureScript.CST.Lexer
import Language.PureScript.CST.Monad
import Language.PureScript.CST.Positions
import Language.PureScript.CST.Types
import Language.PureScript.CST.Utils
import qualified Language.PureScript.Names as N
import Language.PureScript.PSString (PSString)
}
%expect 98
%name parseKind kind
%name parseType type
%name parseExpr expr
%name parseIdent ident
%name parseOperator op
%name parseModuleBody moduleBody
%name parseDecl decl
%partial parseImportDeclP importDeclP
%partial parseDeclP declP
%partial parseExprP exprP
%partial parseTypeP typeP
%partial parseModuleNameP moduleNameP
%partial parseQualIdentP qualIdentP
%partial parseModuleHeader moduleHeader
%partial parseDoStatement doStatement
%partial parseDoExpr doExpr
%partial parseDoNext doNext
%partial parseGuardExpr guardExpr
%partial parseGuardNext guardNext
%partial parseGuardStatement guardStatement
%partial parseClassSuper classSuper
%partial parseClassNameAndFundeps classNameAndFundeps
%partial parseBinderAndArrow binderAndArrow
%tokentype { SourceToken }
%monad { Parser }
%error { parseError }
%lexer { lexer } { SourceToken _ TokEof }
%token
'(' { SourceToken _ TokLeftParen }
')' { SourceToken _ TokRightParen }
'{' { SourceToken _ TokLeftBrace }
'}' { SourceToken _ TokRightBrace }
'[' { SourceToken _ TokLeftSquare }
']' { SourceToken _ TokRightSquare }
'\{' { SourceToken _ TokLayoutStart }
'\}' { SourceToken _ TokLayoutEnd }
'\;' { SourceToken _ TokLayoutSep }
'<-' { SourceToken _ (TokLeftArrow _) }
'->' { SourceToken _ (TokRightArrow _) }
'<=' { SourceToken _ (TokOperator [] sym) | isLeftFatArrow sym }
'=>' { SourceToken _ (TokRightFatArrow _) }
':' { SourceToken _ (TokOperator [] ":") }
'::' { SourceToken _ (TokDoubleColon _) }
'=' { SourceToken _ TokEquals }
'|' { SourceToken _ TokPipe }
'`' { SourceToken _ TokTick }
'.' { SourceToken _ TokDot }
',' { SourceToken _ TokComma }
'_' { SourceToken _ TokUnderscore }
'\\' { SourceToken _ TokBackslash }
'-' { SourceToken _ (TokOperator [] "-") }
'@' { SourceToken _ (TokOperator [] "@") }
'#' { SourceToken _ (TokOperator [] "#") }
'ado' { SourceToken _ (TokLowerName _ "ado") }
'as' { SourceToken _ (TokLowerName [] "as") }
'case' { SourceToken _ (TokLowerName [] "case") }
'class' { SourceToken _ (TokLowerName [] "class") }
'data' { SourceToken _ (TokLowerName [] "data") }
'derive' { SourceToken _ (TokLowerName [] "derive") }
'do' { SourceToken _ (TokLowerName _ "do") }
'else' { SourceToken _ (TokLowerName [] "else") }
'false' { SourceToken _ (TokLowerName [] "false") }
'forall' { SourceToken _ (TokForall ASCII) }
'forallu' { SourceToken _ (TokForall Unicode) }
'foreign' { SourceToken _ (TokLowerName [] "foreign") }
'hiding' { SourceToken _ (TokLowerName [] "hiding") }
'import' { SourceToken _ (TokLowerName [] "import") }
'if' { SourceToken _ (TokLowerName [] "if") }
'in' { SourceToken _ (TokLowerName [] "in") }
'infix' { SourceToken _ (TokLowerName [] "infix") }
'infixl' { SourceToken _ (TokLowerName [] "infixl") }
'infixr' { SourceToken _ (TokLowerName [] "infixr") }
'instance' { SourceToken _ (TokLowerName [] "instance") }
'kind' { SourceToken _ (TokLowerName [] "kind") }
'let' { SourceToken _ (TokLowerName [] "let") }
'module' { SourceToken _ (TokLowerName [] "module") }
'newtype' { SourceToken _ (TokLowerName [] "newtype") }
'of' { SourceToken _ (TokLowerName [] "of") }
'then' { SourceToken _ (TokLowerName [] "then") }
'true' { SourceToken _ (TokLowerName [] "true") }
'type' { SourceToken _ (TokLowerName [] "type") }
'where' { SourceToken _ (TokLowerName [] "where") }
'(->)' { SourceToken _ (TokSymbolArr _) }
'(..)' { SourceToken _ (TokSymbolName [] "..") }
LOWER { SourceToken _ (TokLowerName [] _) }
QUAL_LOWER { SourceToken _ (TokLowerName _ _) }
UPPER { SourceToken _ (TokUpperName [] _) }
QUAL_UPPER { SourceToken _ (TokUpperName _ _) }
SYMBOL { SourceToken _ (TokSymbolName [] _) }
QUAL_SYMBOL { SourceToken _ (TokSymbolName _ _) }
OPERATOR { SourceToken _ (TokOperator [] _) }
QUAL_OPERATOR { SourceToken _ (TokOperator _ _) }
LIT_HOLE { SourceToken _ (TokHole _) }
LIT_CHAR { SourceToken _ (TokChar _ _) }
LIT_STRING { SourceToken _ (TokString _ _) }
LIT_RAW_STRING { SourceToken _ (TokRawString _) }
LIT_INT { SourceToken _ (TokInt _ _) }
LIT_NUMBER { SourceToken _ (TokNumber _ _) }
%%
many(a) :: { NE.NonEmpty a }
: many1(a) { NE.reverse $1 }
many1(a) :: { NE.NonEmpty a }
: a { pure $1 }
| many1(a) a { NE.cons $2 $1 }
manySep(a, sep) :: { NE.NonEmpty a }
: manySep1(a, sep) { NE.reverse $1 }
manySep1(a, sep) :: { NE.NonEmpty a }
: a { pure $1 }
| manySep1(a, sep) sep a { NE.cons $3 $1 }
manySepOrEmpty(a, sep) :: { [a] }
: {- empty -} { [] }
| manySep(a, sep) { NE.toList $1 }
manyOrEmpty(a) :: { [a] }
: {- empty -} { [] }
| many(a) { NE.toList $1 }
sep(a, s) :: { Separated a }
: sep1(a, s) { separated $1 }
sep1(a, s) :: { [(SourceToken, a)] }
: a { [(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 }
moduleName :: { Name N.ModuleName }
: UPPER {% upperToModuleName $1 }
| QUAL_UPPER {% upperToModuleName $1 }
qualProperName :: { QualifiedName (N.ProperName a) }
: UPPER {% toQualifiedName N.ProperName $1 }
| QUAL_UPPER {% toQualifiedName N.ProperName $1 }
properName :: { Name (N.ProperName a) }
: UPPER {% toName N.ProperName $1 }
qualIdent :: { QualifiedName Ident }
: LOWER {% toQualifiedName Ident $1 }
| QUAL_LOWER {% toQualifiedName Ident $1 }
| 'as' {% toQualifiedName Ident $1 }
| 'hiding' {% toQualifiedName Ident $1 }
| 'kind' {% toQualifiedName Ident $1 }
ident :: { Name Ident }
: LOWER {% toName Ident $1 }
| 'as' {% toName Ident $1 }
| 'hiding' {% toName Ident $1 }
| 'kind' {% toName Ident $1 }
qualOp :: { QualifiedName (N.OpName a) }
: OPERATOR {% toQualifiedName N.OpName $1 }
| QUAL_OPERATOR {% toQualifiedName N.OpName $1 }
| '<=' {% toQualifiedName N.OpName $1 }
| '-' {% toQualifiedName N.OpName $1 }
| '#' {% toQualifiedName N.OpName $1 }
| ':' {% toQualifiedName N.OpName $1 }
op :: { Name (N.OpName a) }
: OPERATOR {% toName N.OpName $1 }
| '<=' {% toName N.OpName $1 }
| '-' {% toName N.OpName $1 }
| '#' {% toName N.OpName $1 }
| ':' {% toName N.OpName $1 }
qualSymbol :: { QualifiedName (N.OpName a) }
: SYMBOL {% toQualifiedName N.OpName $1 }
| QUAL_SYMBOL {% toQualifiedName N.OpName $1 }
| '(..)' {% toQualifiedName N.OpName $1 }
symbol :: { Name (N.OpName a) }
: SYMBOL {% toName N.OpName $1 }
| '(..)' {% toName N.OpName $1 }
label :: { Label }
: LOWER { toLabel $1 }
| LIT_STRING { toLabel $1 }
| LIT_RAW_STRING { toLabel $1 }
| 'ado' { toLabel $1 }
| 'as' { toLabel $1 }
| 'case' { toLabel $1 }
| 'class' { toLabel $1 }
| 'data' { toLabel $1 }
| 'derive' { toLabel $1 }
| 'do' { toLabel $1 }
| 'else' { toLabel $1 }
| 'false' { toLabel $1 }
| 'forall' { toLabel $1 }
| 'foreign' { toLabel $1 }
| 'hiding' { toLabel $1 }
| 'import' { toLabel $1 }
| 'if' { toLabel $1 }
| 'in' { toLabel $1 }
| 'infix' { toLabel $1 }
| 'infixl' { toLabel $1 }
| 'infixr' { toLabel $1 }
| 'instance' { toLabel $1 }
| 'kind' { toLabel $1 }
| 'let' { toLabel $1 }
| 'module' { toLabel $1 }
| 'newtype' { toLabel $1 }
| 'of' { toLabel $1 }
| 'then' { toLabel $1 }
| 'true' { toLabel $1 }
| 'type' { toLabel $1 }
| 'where' { toLabel $1 }
hole :: { Name Ident }
: LIT_HOLE {% toName Ident $1 }
string :: { (SourceToken, PSString) }
: LIT_STRING { toString $1 }
| LIT_RAW_STRING { toString $1 }
char :: { (SourceToken, Char) }
: LIT_CHAR { toChar $1 }
number :: { (SourceToken, Either Integer Double) }
: LIT_INT { toNumber $1 }
| LIT_NUMBER { toNumber $1 }
int :: { (SourceToken, Integer) }
: LIT_INT { toInt $1 }
boolean :: { (SourceToken, Bool) }
: 'true' { toBoolean $1 }
| 'false' { toBoolean $1 }
kind :: { Kind () }
: kind1 { $1 }
| kind1 '->' kind { KindArr () $1 $2 $3 }
kind1 :: { Kind () }
: qualProperName { KindName () $1 }
| '#' kind1 { KindRow () $1 $2 }
| '(' kind ')' { KindParens () (Wrapped $1 $2 $3) }
type :: { Type () }
: type1 { $1 }
| type1 '::' kind { TypeKinded () $1 $2 $3 }
type1 :: { Type () }
: type2 { $1 }
| forall many(typeVarBinding) '.' type1 { TypeForall () $1 $2 $3 $4 }
type2 :: { Type () }
: type3 { $1 }
| type3 '->' type1 { TypeArr () $1 $2 $3 }
| type3 '=>' type1 {% do cs <- toConstraint $1; pure $ TypeConstrained () cs $2 $3 }
type3 :: { Type () }
: type4 { $1 }
| type3 qualOp type4 { TypeOp () $1 $2 $3 }
type4 :: { Type () }
: typeAtom { $1 }
| type4 typeAtom { TypeApp () $1 $2 }
typeAtom :: { Type ()}
: '_' { TypeWildcard () $1 }
| ident { TypeVar () $1 }
| qualProperName { TypeConstructor () $1 }
| qualSymbol { TypeOpName () $1 }
| string { uncurry (TypeString ()) $1 }
| hole { TypeHole () $1 }
| '(->)' { TypeArrName () $1 }
| '{' row '}' { TypeRecord () (Wrapped $1 $2 $3) }
| '(' row ')' { TypeRow () (Wrapped $1 $2 $3) }
| '(' type1 ')' { TypeParens () (Wrapped $1 $2 $3) }
| '(' typeKindedAtom '::' kind ')' { TypeParens () (Wrapped $1 (TypeKinded () $2 $3 $4) $5) }
-- Due to a conflict between row syntax and kinded type syntax, we require
-- kinded type variables to be wrapped in parens. Thus `(a :: Foo)` is always a
-- row, and to annotate `a` with kind `Foo`, one must use `((a) :: Foo)`.
typeKindedAtom :: { Type () }
: '_' { TypeWildcard () $1 }
| qualProperName { TypeConstructor () $1 }
| qualSymbol { TypeOpName () $1 }
| hole { TypeHole () $1 }
| '{' row '}' { TypeRecord () (Wrapped $1 $2 $3) }
| '(' row ')' { TypeRow () (Wrapped $1 $2 $3) }
| '(' type1 ')' { TypeParens () (Wrapped $1 $2 $3) }
| '(' typeKindedAtom '::' kind ')' { TypeParens () (Wrapped $1 (TypeKinded () $2 $3 $4) $5) }
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)) }
rowLabel :: { Labeled Label (Type ()) }
: label '::' type { Labeled $1 $2 $3 }
typeVarBinding :: { TypeVarBinding () }
: ident { TypeVarName $1 }
| '(' ident '::' kind ')' { TypeVarKinded (Wrapped $1 (Labeled $2 $3 $4) $5) }
forall :: { SourceToken }
: 'forall' { $1 }
| 'forallu' { $1 }
exprWhere :: { Where () }
: expr { Where $1 Nothing }
| expr 'where' '\{' manySep(letBinding, '\;') '\}' { Where $1 (Just ($2, $4)) }
expr :: { Expr () }
: expr1 { $1 }
| expr1 '::' type { ExprTyped () $1 $2 $3 }
expr1 :: { Expr () }
: expr2 { $1 }
| expr1 qualOp expr2 { ExprOp () $1 $2 $3 }
expr2 :: { Expr () }
: expr3 { $1 }
| expr2 '`' exprBacktick '`' expr3 { ExprInfix () $1 (Wrapped $2 $3 $4) $5 }
exprBacktick :: { Expr () }
: expr3 { $1 }
| exprBacktick qualOp expr3 { ExprOp () $1 $2 $3 }
expr3 :: { Expr () }
: expr4 { $1 }
| '-' expr3 { ExprNegate () $1 $2 }
expr4 :: { Expr () }
: expr5 { $1 }
| expr4 expr5
{ -- Record application/updates can introduce a function application
-- associated to the right, so we need to correct it.
case $2 of
ExprApp _ lhs rhs ->
ExprApp () (ExprApp () $1 lhs) rhs
_ -> ExprApp () $1 $2
}
expr5 :: { Expr () }
: expr6 { $1 }
| 'if' expr 'then' expr 'else' expr { ExprIf () (IfThenElse $1 $2 $3 $4 $5 $6) }
| doBlock { ExprDo () $1 }
| adoBlock 'in' expr { ExprAdo () $ uncurry AdoBlock $1 $2 $3 }
| '\\' many(binderAtom) '->' expr { ExprLambda () (Lambda $1 $2 $3 $4) }
| 'let' '\{' manySep(letBinding, '\;') '\}' 'in' expr { ExprLet () (LetIn $1 $3 $5 $6) }
| 'case' sep(expr, ',') 'of' '\{' manySep(caseBranch, '\;') '\}' { ExprCase () (CaseOf $1 $2 $3 $5) }
-- These special cases handle some idiosynchratic syntax that the current
-- parser allows. Technically the parser allows the rhs of a case branch to be
-- at any level, but this is ambiguous. We allow it in the case of a singleton
-- case, since this is used in the wild.
| 'case' sep(expr, ',') 'of' '\{' sep(binder1, ',') '->' '\}' exprWhere
{ ExprCase () (CaseOf $1 $2 $3 (pure ($5, Unconditional $6 $8))) }
| 'case' sep(expr, ',') 'of' '\{' sep(binder1, ',') '\}' guardedCase
{ ExprCase () (CaseOf $1 $2 $3 (pure ($5, $7))) }
expr6 :: { Expr () }
: expr7 { $1 }
| expr7 '{' '}' { ExprApp () $1 (ExprRecord () (Wrapped $2 Nothing $3)) }
| expr7 '{' sep(recordUpdateOrLabel, ',') '}'
{% toRecordFields $3 >>= \case
Left xs -> pure $ ExprApp () $1 (ExprRecord () (Wrapped $2 (Just xs) $4))
Right xs -> pure $ ExprRecordUpdate () $1 (Wrapped $2 xs $4)
}
expr7 :: { Expr () }
: exprAtom { $1 }
| exprAtom '.' sep(label, '.') { ExprRecordAccessor () (RecordAccessor $1 $2 $3) }
exprAtom :: { Expr () }
: '_' { ExprSection () $1 }
| hole { ExprHole () $1 }
| qualIdent { ExprIdent () $1 }
| qualProperName { ExprConstructor () $1 }
| qualSymbol { ExprOpName () $1 }
| boolean { uncurry (ExprBoolean ()) $1 }
| char { uncurry (ExprChar ()) $1 }
| string { uncurry (ExprString ()) $1 }
| number { uncurry (ExprNumber ()) $1 }
| delim('[', expr, ',', ']') { ExprArray () $1 }
| delim('{', recordLabel, ',', '}') { ExprRecord () $1 }
| '(' expr ')' { ExprParens () (Wrapped $1 $2 $3) }
recordLabel :: { RecordLabeled (Expr ()) }
: label {% fmap RecordPun . toName Ident $ lblTok $1 }
| label '=' expr {% addFailure [$2] ErrRecordUpdateInCtr *> pure (RecordPun $ unexpectedName $ lblTok $1) }
| label ':' expr { RecordField $1 $2 $3 }
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)) }
recordUpdate :: { RecordUpdate () }
: label '=' expr { RecordUpdateLeaf $1 $2 $3 }
| label '{' sep(recordUpdate, ',') '}' { RecordUpdateBranch $1 (Wrapped $2 $3 $4) }
letBinding :: { LetBinding () }
: ident '::' type { LetBindingSignature () (Labeled $1 $2 $3) }
| ident guardedDecl { LetBindingName () (ValueBindingFields $1 [] $2) }
| ident many(binderAtom) guardedDecl { LetBindingName () (ValueBindingFields $1 (NE.toList $2) $3) }
| binder1 '=' exprWhere { LetBindingPattern () $1 $2 $3 }
caseBranch :: { (Separated (Binder ()), Guarded ()) }
: sep(binder1, ',') guardedCase { ($1, $2) }
guardedDecl :: { Guarded () }
: '=' exprWhere { Unconditional $1 $2 }
| many(guardedDeclExpr) { Guarded $1 }
guardedDeclExpr :: { GuardedExpr () }
: guard '=' exprWhere { uncurry GuardedExpr $1 $2 $3 }
guardedCase :: { Guarded () }
: '->' exprWhere { Unconditional $1 $2 }
| many(guardedCaseExpr) { Guarded $1 }
guardedCaseExpr :: { GuardedExpr () }
: guard '->' exprWhere { uncurry GuardedExpr $1 $2 $3 }
-- Do/Ado statements and pattern guards require unbounded lookahead due to many
-- conflicts between `binder` and `expr` syntax. For example `Foo a b c` can
-- either be a constructor `binder` or several `expr` applications, and we won't
-- know until we see a `<-` or layout separator.
--
-- One way to resolve this would be to parse a `binder` as an `expr` and then
-- reassociate it after the fact. However this means we can't use the `binder`
-- productions to parse it, so we'd have to maintain an ad-hoc handwritten
-- parser which is very difficult to audit.
--
-- As an alternative we introduce some backtracking. Using %partial parsers and
-- monadic reductions, we can invoke productions manually and use the
-- backtracking `tryPrefix` combinator. Binders are generally very short in
-- comparison to expressions, so the cost is modest.
--
-- doBlock
-- : 'do' '\{' manySep(doStatement, '\;') '\}'
--
-- doStatement
-- : 'let' '\{' manySep(letBinding, '\;') '\}'
-- | expr
-- | binder '<-' expr
--
-- guard
-- : '|' sep(patternGuard, ',')
--
-- patternGuard
-- : expr1
-- | binder '<-' expr1
--
doBlock :: { DoBlock () }
: 'do' '\{'
{%% revert $ do
res <- parseDoStatement
when (null res) $ addFailure [$2] ErrEmptyDo
pure $ DoBlock $1 $ NE.fromList res
}
adoBlock :: { (SourceToken, [DoStatement ()]) }
: 'ado' '\{' '\}' { ($1, []) }
| 'ado' '\{'
{%% revert $ fmap ($1,) parseDoStatement }
doStatement :: { [DoStatement ()] }
: 'let' '\{' manySep(letBinding, '\;') '\}'
{%^ revert $ fmap (DoLet $1 $3 :) parseDoNext }
| {- empty -}
{%^ revert $ do
stmt <- tryPrefix parseBinderAndArrow parseDoExpr
let
ctr = case stmt of
(Just (binder, sep), expr) ->
(DoBind binder sep expr :)
(Nothing, expr) ->
(DoDiscard expr :)
fmap ctr parseDoNext
}
doExpr :: { Expr () }
: expr {%^ revert $ pure $1 }
doNext :: { [DoStatement ()] }
: '\;' {%^ revert parseDoStatement }
| '\}' {%^ revert $ pure [] }
guard :: { (SourceToken, Separated (PatternGuard ())) }
: '|' {%% revert $ fmap (($1,) . uncurry Separated) parseGuardStatement }
guardStatement :: { (PatternGuard (), [(SourceToken, PatternGuard ())]) }
: {- empty -}
{%^ revert $ do
grd <- fmap (uncurry PatternGuard) $ tryPrefix parseBinderAndArrow parseGuardExpr
fmap (grd,) parseGuardNext
}
guardExpr :: { Expr() }
: expr1 {%^ revert $ pure $1 }
guardNext :: { [(SourceToken, PatternGuard ())] }
: ',' {%^ revert $ fmap (\(g, gs) -> ($1, g) : gs) parseGuardStatement }
| {- empty -} {%^ revert $ pure [] }
binderAndArrow :: { (Binder (), SourceToken) }
: binder '<-' {%^ revert $ pure ($1, $2) }
binder :: { Binder () }
: binder1 { $1 }
| binder1 '::' type { BinderTyped () $1 $2 $3 }
binder1 :: { Binder () }
: binder2 { $1 }
| binder1 qualOp binder2 { BinderOp () $1 $2 $3 }
binder2 :: { Binder () }
: many(binderAtom) {% toBinderConstructor $1 }
binderAtom :: { Binder () }
: '_' { BinderWildcard () $1 }
| ident { BinderVar () $1 }
| ident '@' binderAtom { BinderNamed () $1 $2 $3 }
| qualProperName { BinderConstructor () $1 [] }
| boolean { uncurry (BinderBoolean ()) $1 }
| char { uncurry (BinderChar ()) $1 }
| string { uncurry (BinderString ()) $1 }
| number { uncurry (BinderNumber () Nothing) $1 }
| '-' number { uncurry (BinderNumber () (Just $1)) $2 }
| delim('[', binder, ',', ']') { BinderArray () $1 }
| delim('{', recordBinder, ',', '}') { BinderRecord () $1 }
| '(' binder ')' { BinderParens () (Wrapped $1 $2 $3) }
recordBinder :: { RecordLabeled (Binder ()) }
: label {% fmap RecordPun . toName Ident $ lblTok $1 }
| label '=' binder {% addFailure [$2] ErrRecordUpdateInCtr *> pure (RecordPun $ unexpectedName $ lblTok $1) }
| label ':' binder { RecordField $1 $2 $3 }
-- By splitting up the module header from the body, we can incrementally parse
-- just the header, and then continue parsing the body while still sharing work.
moduleHeader :: { Module () }
: 'module' moduleName exports 'where' '\{' moduleImports
{ (Module () $1 $2 $3 $4 $6 [] []) }
moduleBody :: { ([Declaration ()], [Comment LineFeed]) }
: moduleDecls '\}'
{%^ \(SourceToken ann _) -> pure (snd $1, tokLeadingComments ann) }
moduleImports :: { [ImportDecl ()] }
: importDecls importDecl '\}'
{%^ revert $ pushBack $3 *> pure (reverse ($2 : $1)) }
| importDecls
{%^ revert $ pure (reverse $1) }
importDecls :: { [ImportDecl ()] }
: importDecls importDecl '\;' { $2 : $1 }
| {- empty -} { [] }
moduleDecls :: { ([ImportDecl ()], [Declaration ()]) }
: manySep(moduleDecl, '\;') {% toModuleDecls $ NE.toList $1 }
| {- empty -} { ([], []) }
moduleDecl :: { TmpModuleDecl a }
: importDecl { TmpImport $1 }
| sep(decl, declElse) { TmpChain $1 }
declElse :: { SourceToken }
: 'else' { $1 }
| 'else' '\;' { $1 }
exports :: { Maybe (DelimitedNonEmpty (Export ())) }
: {- empty -} { Nothing }
| '(' sep(export, ',') ')' { Just (Wrapped $1 $2 $3) }
export :: { Export () }
: ident { ExportValue () $1 }
| symbol { ExportOp () $1 }
| properName { ExportType () $1 Nothing }
| properName dataMembers { ExportType () $1 (Just $2) }
| 'type' symbol { ExportTypeOp () $1 $2 }
| 'class' properName { ExportClass () $1 $2 }
| 'kind' properName { ExportKind () $1 $2 }
| 'module' moduleName { ExportModule () $1 $2 }
dataMembers :: { (DataMembers ()) }
: '(..)' { DataAll () $1 }
| '(' ')' { DataEnumerated () (Wrapped $1 Nothing $2) }
| '(' sep(properName, ',') ')' { DataEnumerated () (Wrapped $1 (Just $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) }
import :: { Import () }
: ident { ImportValue () $1 }
| symbol { ImportOp () $1 }
| properName { ImportType () $1 Nothing }
| properName dataMembers { ImportType () $1 (Just $2) }
| 'type' symbol { ImportTypeOp () $1 $2 }
| 'class' properName { ImportClass () $1 $2 }
| 'kind' properName { ImportKind () $1 $2 }
decl :: { Declaration () }
: dataHead { DeclData () $1 Nothing }
| dataHead '=' sep(dataCtor, '|') { DeclData () $1 (Just ($2, $3)) }
| typeHead '=' type {% checkNoWildcards $3 *> pure (DeclType () $1 $2 $3) }
| newtypeHead '=' properName typeAtom {% checkNoWildcards $4 *> pure (DeclNewtype () $1 $2 $3 $4) }
| classHead {% checkFundeps $1 *> pure (DeclClass () $1 Nothing) }
| classHead 'where' '\{' manySep(classMember, '\;') '\}' {% checkFundeps $1 *> pure (DeclClass () $1 (Just ($2, $4))) }
| instHead { DeclInstanceChain () (Separated (Instance $1 Nothing) []) }
| instHead 'where' '\{' manySep(instBinding, '\;') '\}' { DeclInstanceChain () (Separated (Instance $1 (Just ($2, $4))) []) }
| 'derive' instHead { DeclDerive () $1 Nothing $2 }
| 'derive' 'newtype' instHead { DeclDerive () $1 (Just $2) $3 }
| ident '::' type { DeclSignature () (Labeled $1 $2 $3) }
| ident manyOrEmpty(binderAtom) guardedDecl { DeclValue () (ValueBindingFields $1 $2 $3) }
| fixity { DeclFixity () $1 }
| 'foreign' 'import' foreign { DeclForeign () $1 $2 $3 }
dataHead :: { DataHead () }
: 'data' properName manyOrEmpty(typeVarBinding) { DataHead $1 $2 $3 }
typeHead :: { DataHead () }
: 'type' properName manyOrEmpty(typeVarBinding) { DataHead $1 $2 $3 }
newtypeHead :: { DataHead () }
: 'newtype' properName manyOrEmpty(typeVarBinding) { DataHead $1 $2 $3 }
dataCtor :: { DataCtor () }
: properName manyOrEmpty(typeAtom)
{% for_ $2 checkNoWildcards *> pure (DataCtor () $1 $2) }
-- Class head syntax requires unbounded lookahead due to a conflict between
-- row syntax and `typeVarBinding`. `(a :: B)` is either a row in `constraint`
-- where `B` is a type or a `typeVarBinding` where `B` is a kind. We must see
-- either a `<=`, `where`, or layout delimiter before deciding which it is.
--
-- classHead
-- : 'class' classNameAndFundeps
-- | 'class' constraints '<=' classNameAndFundeps
--
classHead :: { ClassHead () }
: 'class'
{%% revert $ do
let
ctr (super, (name, vars, fundeps)) =
ClassHead $1 super name vars fundeps
fmap ctr $ tryPrefix parseClassSuper parseClassNameAndFundeps
}
classSuper
: constraints '<=' {%^ revert $ pure ($1, $2) }
classNameAndFundeps :: { (Name (N.ProperName 'N.ClassName), [TypeVarBinding ()], Maybe (SourceToken, Separated ClassFundep)) }
: properName manyOrEmpty(typeVarBinding) fundeps {%^ revert $ pure ($1, $2, $3) }
fundeps :: { Maybe (SourceToken, Separated ClassFundep) }
: {- empty -} { Nothing }
| '|' sep(fundep, ',') { Just ($1, $2) }
fundep :: { ClassFundep }
: '->' many(ident) { FundepDetermined $1 $2 }
| many(ident) '->' many(ident) { FundepDetermines $1 $2 $3 }
classMember :: { Labeled (Name Ident) (Type ()) }
: ident '::' type {% checkNoWildcards $3 *> pure (Labeled $1 $2 $3) }
instHead :: { InstanceHead () }
: 'instance' ident '::' constraints '=>' qualProperName manyOrEmpty(typeAtom)
{ InstanceHead $1 $2 $3 (Just ($4, $5)) $6 $7 }
| 'instance' ident '::' qualProperName manyOrEmpty(typeAtom)
{ InstanceHead $1 $2 $3 Nothing $4 $5 }
constraints :: { OneOrDelimited (Constraint ()) }
: constraint { One $1 }
| '(' sep(constraint, ',') ')' { Many (Wrapped $1 $2 $3) }
constraint :: { Constraint () }
: qualProperName manyOrEmpty(typeAtom) {% for_ $2 checkNoWildcards *> for_ $2 checkNoForalls *> pure (Constraint () $1 $2) }
| '(' constraint ')' { ConstraintParens () (Wrapped $1 $2 $3) }
instBinding :: { InstanceBinding () }
: ident '::' type { InstanceBindingSignature () (Labeled $1 $2 $3) }
| ident manyOrEmpty(binderAtom) guardedDecl { InstanceBindingName () (ValueBindingFields $1 $2 $3) }
fixity :: { FixityFields }
: infix int qualIdent 'as' op { FixityFields $1 $2 (FixityValue (fmap Left $3) $4 $5) }
| infix int qualProperName 'as' op { FixityFields $1 $2 (FixityValue (fmap Right $3) $4 $5) }
| infix int 'type' qualProperName 'as' op { FixityFields $1 $2 (FixityType $3 $4 $5 $6) }
infix :: { (SourceToken, Fixity) }
: 'infix' { ($1, Infix) }
| 'infixl' { ($1, Infixl) }
| 'infixr' { ($1, Infixr) }
foreign :: { Foreign () }
: ident '::' type { ForeignValue (Labeled $1 $2 $3) }
| 'data' properName '::' kind { ForeignData $1 (Labeled $2 $3 $4) }
| 'kind' properName { ForeignKind $1 $2 }
-- Partial parsers which can be combined with combinators for adhoc use. We need
-- to revert the lookahead token so that it doesn't consume an extra token
-- before succeeding.
importDeclP :: { ImportDecl () }
: importDecl {%^ revert $ pure $1 }
declP :: { Declaration () }
: decl {%^ revert $ pure $1 }
exprP :: { Expr () }
: expr {%^ revert $ pure $1 }
typeP :: { Type () }
: type {%^ revert $ pure $1 }
moduleNameP :: { Name N.ModuleName }
: moduleName {%^ revert $ pure $1 }
qualIdentP :: { QualifiedName Ident }
: qualIdent {%^ revert $ pure $1 }
{
lexer :: (SourceToken -> Parser a) -> Parser a
lexer k = munch >>= k
parse :: Text -> Either (NE.NonEmpty ParserError) (Module ())
parse = resFull <=< parseModule . lex
data PartialResult a = PartialResult
{ resPartial :: a
, resFull :: Either (NE.NonEmpty ParserError) a
} deriving (Functor)
parseModule :: [LexResult] -> Either (NE.NonEmpty ParserError) (PartialResult (Module ()))
parseModule toks = fmap (\header -> PartialResult header (parseFull header)) headerRes
where
(st, headerRes) =
runParser (ParserState (toks) []) parseModuleHeader
parseFull header = do
(decls, trailing) <- snd $ runParser st parseModuleBody
pure $ header
{ modDecls = decls
, modTrailingComments = trailing
}
}