language-dickinson-1.1.0.2: src/Language/Dickinson/Parser.y
{
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
module Language.Dickinson.Parser ( parse
, parseWithMax
, parseWithCtx
, parseWithInitCtx
, parseReplWithCtx
, parseExpressionWithCtx
, ParseError (..)
) where
import Data.Bifunctor (first)
import Control.DeepSeq (NFData)
import Control.Exception (Exception)
import Control.Monad.Except (ExceptT, runExceptT, throwError)
import Control.Monad.Trans.Class (lift)
import qualified Data.ByteString.Lazy as BSL
import Data.Foldable (toList)
import qualified Data.List.NonEmpty as NE
import Data.List.NonEmpty (NonEmpty ((:|)), (<|))
import Data.Maybe (mapMaybe)
import Data.Semigroup ((<>))
import qualified Data.Text as T
import Data.Text.Encoding (decodeUtf8)
import Prettyprinter (Pretty (pretty), (<+>))
import Data.Tuple.Ext (fst4)
import Data.Typeable (Typeable)
import GHC.Generics (Generic)
import Language.Dickinson.Lexer
import Language.Dickinson.Name hiding (loc)
import Language.Dickinson.Type
import Language.Dickinson.Unique
}
%name parseDickinson Dickinson
%name parseExpression Expression
%name parseRepl DeclarationOrExpression
%tokentype { Token AlexPosn }
%error { parseError }
%monad { Parse } { (>>=) } { pure }
%lexer { lift alexMonadScan >>= } { EOF _ }
%token
lparen { TokSym $$ LParen }
rparen { TokSym $$ RParen }
vbar { TokSym $$ VBar }
lsqbracket { TokSym $$ LSqBracket }
rsqbracket { TokSym $$ RSqBracket }
rbracket { TokSym $$ RBracket }
strBegin { TokSym $$ StrBegin }
multiStrBegin { TokSym $$ MultiStrBegin }
multiStrEnd { TokSym $$ MultiStrEnd }
strEnd { TokSym $$ StrEnd }
arrow { TokSym $$ Arrow }
dollar { TokSym $$ DollarSign }
comma { TokSym $$ Comma }
underscore { TokSym $$ Underscore }
colon { TokSym $$ Colon }
declBreak { TokSym $$ DeclBreak }
eq { TokSym $$ Eq }
beginInterp { TokSym $$ BeginInterp }
endInterp { TokSym $$ EndInterp }
def { TokKeyword $$ KwDef }
let { TokKeyword $$ KwLet }
branch { TokKeyword $$ KwBranch }
oneof { TokKeyword $$ KwOneof }
include { TokKeyword $$ KwInclude }
lambda { TokKeyword $$ KwLambda }
match { TokKeyword $$ KwMatch }
flatten { TokKeyword $$ KwFlatten }
tydecl { TokKeyword $$ KwTyDecl }
builtin { $$@(TokBuiltin _ _) }
text { TokKeyword $$ KwText }
ident { $$@(TokIdent _ _) }
tyIdent { $$@(TokTyCons _ _) }
strChunk { $$@(TokStrChunk _ _) }
stringLiteral { $$@(TokString _ _) }
num { TokDouble _ $$ }
%%
many(p)
: many(p) p { $2 : $1 }
| { [] }
some(p)
: many(p) p { $2 :| $1 }
sepBy(p,q)
: sepBy(p,q) q p { $3 <| $1 }
| p q p { $3 :| [$1] }
parens(p)
: lparen p rparen { $2 }
brackets(p)
: lsqbracket p rsqbracket { $2 }
Dickinson :: { Dickinson AlexPosn }
: many(parens(Import)) declBreak many(Declaration) { Dickinson (reverse $1) (reverse $3) }
TyCons :: { NonEmpty (TyName AlexPosn) }
: sepBy(tyIdent,vbar) { fmap tyIdent $1 }
Declaration :: { Declaration AlexPosn }
: lparen def Name Expression rparen { Define $2 $3 $4 }
| tydecl Name eq TyCons { TyDecl $1 $2 (NE.reverse $4) }
Import :: { Import AlexPosn }
: include Name { Import $1 $2 }
Name :: { Name AlexPosn }
: ident { ident $1 }
Type :: { DickinsonTy AlexPosn }
: text { TyText $1 }
| arrow Type Type { TyFun $1 $2 $3 }
| lparen sepBy(Type,comma) rparen { TyTuple $1 (NE.reverse $2) }
| ident { TyNamed (loc $1) (ident $1) }
| parens(Type) { $1 }
Bind :: { (Name AlexPosn, Expression AlexPosn) }
: Name Expression { ($1, $2) }
PatternBind :: { (Pattern AlexPosn, Expression AlexPosn) }
: Pattern Expression { ($1, $2) }
Interp :: { Expression AlexPosn }
Interp : strChunk { StrChunk (loc $1) (str $1) }
| beginInterp Expression endInterp { $2 }
Pattern :: { Pattern AlexPosn }
: ident { PatternVar (loc $1) (ident $1) }
| lparen sepBy(Pattern,comma) rparen { PatternTuple $1 (NE.reverse $2) }
| underscore { Wildcard $1 }
| tyIdent { PatternCons (loc $1) (tyIdent $1) }
| lparen sepBy(Pattern,vbar) rparen { OrPattern $1 (NE.reverse $2) }
Expression :: { Expression AlexPosn }
: branch some(parens(WeightedLeaf)) { Choice $1 (NE.reverse $2) }
| oneof some(parens(Leaf)) { Choice $1 (NE.reverse (weight $2)) }
| let some(brackets(Bind)) Expression { Let $1 (NE.reverse $2) $3 }
| lambda Name Type Expression { Lambda $1 $2 $3 $4 }
| ident { Var (loc $1) (ident $1) }
| builtin { BuiltinFn (loc $1) (builtin $1) }
| stringLiteral { Literal (loc $1) (str $1) }
| strBegin some(Interp) strEnd { Interp $1 (toList $ NE.reverse $2) }
| multiStrBegin some(Interp) multiStrEnd { MultiInterp $1 (processMultiChunks $ toList $ NE.reverse $2) }
| rbracket many(Expression) { Concat $1 (reverse $2) }
| dollar Expression Expression { Apply $1 $2 $3 }
| lparen sepBy(Expression,comma) rparen { Tuple $1 (NE.reverse $2) }
| match Expression some(brackets(PatternBind)) { Match $1 $2 (NE.reverse $3) }
| flatten Expression { Flatten $1 $2 }
| Expression colon Type { Annot $2 $1 $3 }
| tyIdent { Constructor (loc $1) (tyIdent $1) }
| parens(Expression) { $1 }
WeightedLeaf :: { (Double, Expression AlexPosn) }
: vbar num Expression { ($2, $3) }
| num Expression { ($1, $2) }
Leaf :: { Expression AlexPosn }
: vbar Expression { $2 }
DeclarationOrExpression :: { Either (Declaration AlexPosn) (Expression AlexPosn) }
: Expression { Right $1 }
| Declaration { Left $1 }
{
countSpaces :: T.Text -> Int
countSpaces = T.length . T.takeWhile (== ' ')
dropDoubleNewlines :: [T.Text] -> [T.Text]
dropDoubleNewlines ("":t:ts) | " " `T.isPrefixOf` t = dropDoubleNewlines (t:ts)
dropDoubleNewlines (t:ts) = t : dropDoubleNewlines ts
dropDoubleNewlines [] = []
-- minimum indentation (relevant in a string containing newline characters)
minIndent :: T.Text -> Int
minIndent t = minimum (maxBound : fmap countSpaces (dropDoubleNewlines $ T.lines t)) -- reduce duplicates
minIndentExpr :: Expression a -> Maybe Int
minIndentExpr (StrChunk _ t) | "\n" `T.isInfixOf` t = Just $ minIndent $ T.tail $ T.dropWhile (/= '\n') t -- tail because T.lines "\n hello" is ["", " hello"]
minIndentExpr _ = Nothing
mapStrChunk :: (T.Text -> T.Text) -> Expression a -> Expression a
mapStrChunk f (StrChunk l t) = StrChunk l (f t)
mapStrChunk _ e = e
minIndentChunks :: [Expression a] -> Int
minIndentChunks es =
minimum (maxBound : mapMaybe minIndentExpr es)
processMultiChunks :: [Expression a] -> [Expression a]
processMultiChunks es = {-# SCC "processMultiChunks" #-}
let toStrip = minIndentChunks es
in let needle = "\n" <> T.replicate toStrip " "
in mapStrChunk (T.replace needle "\n") <$> es
weight :: NonEmpty (Expression a) -> NonEmpty (Double, Expression a)
weight es = (recip, ) <$> es
where recip = 1 / (fromIntegral $ length es)
parseError :: Token AlexPosn -> Parse a
parseError = throwError . Unexpected
data ParseError a = Unexpected (Token a)
| LexErr String
deriving (Generic, NFData)
instance Pretty a => Pretty (ParseError a) where
pretty (Unexpected tok) = pretty (loc tok) <+> "Unexpected" <+> pretty tok
pretty (LexErr str) = pretty (T.pack str)
instance Pretty a => Show (ParseError a) where
show = show . pretty
instance (Pretty a, Typeable a) => Exception (ParseError a)
type Parse = ExceptT (ParseError AlexPosn) Alex
parse :: BSL.ByteString -> Either (ParseError AlexPosn) (Dickinson AlexPosn)
parse = fmap snd . parseWithMax
parseReplWithCtx :: BSL.ByteString -> AlexUserState -> Either (ParseError AlexPosn) (AlexUserState, Either (Declaration AlexPosn) (Expression AlexPosn))
parseReplWithCtx = parseWithInitSt parseRepl
parseExpressionWithCtx :: BSL.ByteString -> AlexUserState -> Either (ParseError AlexPosn) (AlexUserState, Expression AlexPosn)
parseExpressionWithCtx = parseWithInitSt parseExpression
parseWithInitCtx :: BSL.ByteString -> Either (ParseError AlexPosn) (AlexUserState, Dickinson AlexPosn)
parseWithInitCtx bsl = parseWithCtx bsl alexInitUserState
parseWithCtx :: BSL.ByteString -> AlexUserState -> Either (ParseError AlexPosn) (AlexUserState, Dickinson AlexPosn)
parseWithCtx = parseWithInitSt parseDickinson
parseWithMax :: BSL.ByteString -> Either (ParseError AlexPosn) (UniqueCtx, Dickinson AlexPosn)
parseWithMax = parseWrapper parseDickinson
parseWithInitSt :: Parse a -> BSL.ByteString -> AlexUserState -> Either (ParseError AlexPosn) (AlexUserState, a)
parseWithInitSt parser str st = liftErr $ withAlexSt str st (runExceptT parser)
where liftErr (Left err) = Left (LexErr err)
liftErr (Right (_, Left err)) = Left err
liftErr (Right (i, Right x)) = Right (i, x)
parseWrapper :: Parse a -> BSL.ByteString -> Either (ParseError AlexPosn) (UniqueCtx, a)
parseWrapper parser str = fmap (first fst4) $ liftErr $ runAlexSt str (runExceptT parser)
liftErr :: Either String (b, Either (ParseError a) c) -> Either (ParseError a) (b, c)
liftErr (Left err) = Left (LexErr err)
liftErr (Right (_, Left err)) = Left err
liftErr (Right (i, Right x)) = Right (i, x)
}