Abbie's Haskell compiler
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

457 lines
14 KiB

{
{-# LANGUAGE FlexibleContexts, FlexibleInstances, ViewPatterns #-}
module Frontend.Autogen.Parser where
import qualified Data.Text as T
import Data.Maybe
import Frontend.Parser.Foreign
import Frontend.Lexer.Wrapper
import Frontend.Autogen.Lexer
import Frontend.Lexer.Tokens
import Frontend.Parser.Posn
import Frontend.Syntax
import qualified Prelude
import Prelude hiding (span)
import Debug.Trace
import Control.Monad
import Errors
}
%name exp Exp
%name parseMod Module
%name parseType Type
%tokentype { Token }
%monad { Alex }
%lexer { lexer } { Token TokEof _ _ }
%errorhandlertype explist
%error { parseError }
%token
VAR { Token (TokUnqual VarId _) _ _ }
CON { Token (TokUnqual ConId _) _ _ }
QVAR { Token (TokQual VarId _ _) _ _ }
QCON { Token (TokQual ConId _ _) _ _ }
VARSYM { Token (TokUnqualOp VarId _) _ _ }
CONSYM { Token (TokUnqualOp ConId _) _ _ }
QVARSYM { Token (TokQualOp VarId _ _) _ _ }
QCONSYM { Token (TokQualOp ConId _ _) _ _ }
STRING { Token (TokString _) _ _ }
'eof' { Token TokEof _ _ }
'(' { Token TokOParen _ _ }
')' { Token TokCParen _ _ }
'{' { Token TokOBrace _ _ }
'}' { Token TokCBrace _ _ }
START { Token TokLStart _ _ }
END { Token TokLEnd _ _ }
'[' { Token TokOSquare _ _ }
']' { Token TokCSquare _ _ }
'{-#' { Token TokOPragma _ _ }
'#-}' { Token TokCPragma _ _ }
'\\' { Token TokLambda _ _ }
'->' { Token TokArrow _ _ }
'_' { Token TokUnder _ _ }
'::' { Token TokDoubleColon _ _ }
';' { Token TokSemi _ _ }
'=' { Token TokEqual _ _ }
',' { Token TokComma _ _ }
'`' { Token TokTick _ _ }
'let' { Token TokLet _ _ }
'in' { Token TokIn _ _ }
'data' { Token TokData _ _ }
'case' { Token TokCase _ _ }
'of' { Token TokOf _ _ }
'module' { Token TokModule _ _ }
'where' { Token TokWhere _ _ }
'import' { Token TokImport _ _ }
'as' { Token TokAs _ _ }
'qualified' { Token TokQualified _ _ }
'foreign' { Token TokForeign _ _ }
'export' { Token TokExport _ _ }
'safe' { Token TokSafe _ _ }
'unsafe' { Token TokUnsafe _ _ }
'ccall' { Token TokCCall _ _ }
%%
Exp :: { Exp }
: InfixExp '::' Type { span $1 $3 $ Annot $1 $3 }
| InfixExp { $1 }
InfixExp :: { Exp }
: LeftExp {- ... -} { $1 }
| LeftExp qop InfixExp { span $1 $3 $ Infix $1 $2 $3 }
LeftExp :: { Exp }
: '\\' Apat List(Apat) '->' Exp { span $1 $5 (makeLams ($2:$3) $5) }
| 'let' LaidOutList(Decl) 'in' Exp { span $1 $4 $ Let (thd $2) $4 }
| FuncExp { $1 }
FuncExp :: { Exp }
: FuncExp Aexp { span $1 $2 $ App $1 $2 }
| Aexp { $1 }
Aexp :: { Exp }
: qvar { span $1 $1 $ Ref $1 }
| gcon { span $1 $1 $ Con $1 }
| '(' CommaList1(Exp) ')' { span $1 $3 $ makeTuple $2 }
| STRING { span $1 $1 $ Literal (LitString (getString $1)) }
Type :: { Type }
: Btype '->' Type { span $1 $3 $ Tyarr $1 $3 }
| Btype { $1 }
Btype :: { Type }
: Btype Atype { span $1 $2 $ Tyapp $1 $2 }
| Atype { $1 }
Atype :: { Type }
: qvar { span $1 $1 $ Tyvar $1 }
| gtycon { span $1 $1 $ Tycon $1 }
| '[' Type ']' { span $1 $3 $ Tylist $2 }
| '(' CommaList1(Type) ')' { span $1 $3 $ makeTupleType $2 }
Pat :: { Pat }
: Lpat { $1 }
Lpat :: { Pat }
: Apat { $1 }
| gcon Apat List(Apat) { span $1 (endOfListPos $2 $3) (ConPat $1 ($2 : $3)) }
| Lpat qconop Pat { span $1 $3 $ InfixPat $1 $2 $3 }
Apat :: { Pat }
: VAR { span $1 $1 $ Var (getVar $1) }
| gcon { span $1 $1 $ ConPat $1 [] }
| '_' { span $1 $1 $ Wildcard }
| '(' CommaList1(Pat) ')' { span $1 $3 $ makeTuplePattern $2 }
Decl :: { Decl }
: CommaList1(VAR) '::' Type { TySig (getVar `fmap` $1) $3 (startPosn (head $1)) (endPosn $3) }
| VAR Apat List(Apat) Rhs { FunDecl (getVar $1) ($2:$3) $4 (startPosn $1) (endPosn $4) }
| Pat Rhs { PatDecl $1 $2 (startPosn $1) (endPosn $2) }
Rhs :: { Rhs }
: '=' Exp { BareRhs $2 [] (startPosn $1) (endPosn $2) }
| '=' Exp 'where' LaidOutList(Decl) { BareRhs $2 (thd $4) (startPosn $1) (endPosn $4) }
LaidOutList(p)
: START Opt(Semis) LOLContents(p, END) { (startPosn $1, lolEnd $3, lolList $3) }
| '{' Opt(Semis) LOLContents(p, '}') { (startPosn $1, lolEnd $3, lolList $3) }
LOLContents(p, End)
: p Semis LOLContents(p,End) { lolCons $1 $3 }
| p Opt(Semis) End { lolCons $1 (emptyLol $3) }
| Opt(Semis) End { emptyLol $2 }
Module :: { Module }
: 'module' CON ImportExportList 'where' LaidOutList(ModItem)
{% do { (imports,items) <- spanModuleItems (thd $5)
; pure $ Module { moduleName = toModId (getVar $2)
, moduleExports = fst $3
, moduleImports = imports
, moduleItems = items }
}
}
ImportExportList :: { (Maybe [NamespacedItem ParsedVar], Maybe Posn) }
: {-empty-} { (Nothing, Nothing) }
| '(' CommaList(NSItem) ')' { (Just $2, Just (endPosn $3)) }
NSItem :: { NamespacedItem ParsedVar }
: VAR { IEVar (getVar $1) }
| CON { IECon (getVar $1) }
| 'module' CON { IEModule (getVar $2) }
ModItem :: { Item }
: Decl { ModDecl $1 (startPosn $1) (endPosn $1) }
| Import { ModImport $1 (startPosn $1) (endPosn $1) }
| 'foreign' FfiItem { ModFfi $2 (startPosn $1) (endPosn $2) }
Import :: { ModuleImport ParsedVar }
: 'import' modid ImportExportList
{ Import $2 (fst $3) False Nothing (startPosn $1) (fromMaybe (endPosn $2) (snd $3)) }
| 'import' modid ImportExportList 'as' CON
{ Import $2 (fst $3) False (Just (getVar $5)) (startPosn $1) (endPosn $5) }
| 'import' 'qualified' modid ImportExportList
{ Import $3 (fst $4) True Nothing (startPosn $1) (fromMaybe (endPosn $3) (snd $4)) }
| 'import' 'qualified' modid ImportExportList 'as' CON
{ Import $3 (fst $4) True (Just (getVar $6)) (startPosn $1) (endPosn $6) }
FfiItem :: { FfiItem ParsedVar }
: 'import' CallConv Safety Entity VAR '::' Type
-- 1 2 3 4 5 6 7
{% do { ffiDesc <- traverse parseForeignItem $4
; pure $ FfiImport
{ fiVarName = getVar $5
, fiType = $7
, fiCallConv = $2
, fiSafety = $3
, fiItem = ffiDesc
, fiBegin = startPosn $1
, fiEnd = endPosn $7 }
} }
| 'import' CallConv VAR error
{% do { state <- getUserState
; (here, _, _, _) <- alexGetInput
; let vartk = $3
; case lastToken state of
Just tok -> alexThrow $ \fn ->
emptyError { errorMessage = "malformed foreign import (did you spell safe/unsafe incorrectly?)"
, errorInlineDesc = Just "this token was interpreted as a variable name"
, errorBegin = startPosn vartk
, errorEnd = startPosn vartk
, errorFilename = fn
, errorPointers = pointTo tok tok "so this token should have been part of a type signature"
}
Nothing -> alexError "no last token?"
}
}
| 'export' CallConv Entity VAR '::' Type
-- 1 2 3 4 5 6
{% do { ffiDesc <-
case $3 of
Nothing -> pure Nothing
Just x ->
let Posn l c = startPosn x
in parseCid (Posn l (c + 1)) 0 (getString x)
; pure $ FfiExport
{ fiVarName = getVar $4
, fiType = $6
, fiCallConv = $2
, fiExpCid = ffiDesc
, fiBegin = startPosn $1
, fiEnd = endPosn $6 }
} }
Safety :: { Maybe FfiSafety }
: {-empty-} { Nothing }
| 'safe' { Just Safe }
| 'unsafe' { Just Unsafe }
CallConv :: { FfiCallConv }
: 'ccall' { CC_CCall }
| error
{% do { state <- getUserState
; (here, _, _, _) <- alexGetInput
; case lastToken state of
Just tok -> alexThrow $ \fn ->
emptyError { errorMessage = "ahc only supports the 'ccall' calling convention"
, errorInlineDesc = Just ("unexpected " ++ show (tokenClass tok))
, errorBegin = startPosn tok
, errorEnd = here
, errorFilename = fn
}
Nothing -> alexError "no last token?"
}
}
Entity :: { Maybe Token }
: {-empty-} { Nothing }
| STRING { Just $1 }
Opt(p)
: { () }
| p { () }
Semis
: ';' Semis { () }
| ';' { () }
qvarid :: { ParsedVar }
qvarid
: VAR { getVar $1 }
| QVAR { getVar $1 }
qvar :: { ParsedVar }
: qvarid { $1 }
| '(' qvarsym ')' { span $1 $3 $2 }
qconid :: { ParsedVar }
qconid
: CON { getVar $1 }
| QCON { getVar $1 }
qcon :: { ParsedVar }
qcon
: qconid { $1 }
| '(' qconsym ')' { span $1 $3 $2 }
qvarsym :: { ParsedVar }
qvarsym : VARSYM { getVar $1 }
| QVARSYM { getVar $1 }
qconsym :: { ParsedVar }
qconsym : CONSYM { getVar $1 }
| QCONSYM { getVar $1 }
qvarop :: { ParsedVar }
: qvarsym { $1 }
| '`' qvar '`' { span $1 $3 $2 }
qconop :: { ParsedVar }
: qconsym { $1 }
| '`' qcon '`' { span $1 $3 $2 }
qop :: { ParsedVar }
: qvarop { $1 }
| qconop { $1 }
gcon :: { ParsedVar }
: qcon { $1 }
| '(' Commas ')' { BuiltinId { varId = tupleConSymName $2
, varBuiltin = BuiltinTuple $2
, varBegin = startPosn $1
, varEnd = endPosn $3 }
}
| '[' ']' { BuiltinId { varId = T.pack "[]"
, varBuiltin = BuiltinNil
, varBegin = startPosn $1
, varEnd = endPosn $2 }
}
gtycon :: { ParsedVar }
: gcon { $1 }
| '(' '->' ')' { BuiltinId { varId = T.pack "[]"
, varBuiltin = BuiltinArrow
, varBegin = startPosn $1
, varEnd = endPosn $2 }
}
Commas :: { Int }
: {- empty -} { 0 }
| ',' Commas { (let x = $2 in x `seq` 1 + x) }
modid : qconid { toModId $1 }
List(p)
: {-empty-} { [] }
| p List(p) { $1:$2 }
CommaList(p)
: {-empty-} { [] }
| p { [$1] }
| p ',' CommaList(p) { $1:$3 }
CommaList1(p)
: p { [$1] }
| p ',' CommaList(p) { $1:$3 }
Block(p)
: START p END { (startPosn $1, endPosn $3, $2) }
| '{' p '}' { (startPosn $1, endPosn $3, $2) }
{
type Exp = FeExpr ParsedVar
type Pat = FePat ParsedVar
type Decl = FeDecl ParsedVar
type Type = FeType ParsedVar
type Rhs = FeRhs ParsedVar
type Module = FeModule ParsedVar
type Item = ModuleItem ParsedVar
lexer cont = do
tok <- alexMonadScan
mapUserState $ \s -> s { lastToken = Just tok }
cont tok
parseError (token, expected) = do
(here, _, _, _) <- alexGetInput
alexThrow $ \fn -> emptyError { errorMessage = "expecting one of: " ++ unwords expected
, errorInlineDesc = Just ("unexpected " ++ show (tokenClass token))
, errorBegin = startPosn token
, errorEnd = here
, errorFilename = fn
}
makeLams xs b = foldr Lam b xs
getVar :: Token -> ParsedVar
getVar tok@(Token (TokQual _ p s) _ _) = QualVar { varId = s, varPrefix = p, varBegin = startPosn tok, varEnd = endPosn tok }
getVar tok@(Token (TokUnqual _ s) _ _) = UnqualVar { varId = s, varBegin = startPosn tok, varEnd = endPosn tok }
getVar tok@(Token (TokQualOp _ p s) _ _) = QualVar { varId = s, varPrefix = p, varBegin = startPosn tok, varEnd = endPosn tok }
getVar tok@(Token (TokUnqualOp _ s) _ _) = UnqualVar { varId = s, varBegin = startPosn tok, varEnd = endPosn tok }
getVar _ = error "getVar non-var"
getString (Token (TokString s) _ _) = s
getString _ = error "getString non-string"
data LOL a = LOL { lolEnd :: Posn, lolList :: [a] }
emptyLol :: HasPosn x => x -> LOL a
emptyLol t = LOL (endPosn t) []
lolCons :: a -> LOL a -> LOL a
lolCons x (LOL p xs) = LOL p (x:xs)
makeTupleType [x] =
case x of
SPType ParenType{} _ _ -> x
_ -> ParenType x
makeTupleType xs = Tytup xs
makeTuplePattern [x] =
case x of
SPPat ParenPat{} _ _ -> x
_ -> ParenPat x
makeTuplePattern xs = TupPat xs
makeTuple [x] =
case x of
SPExpr ParenExp{} _ _ -> x
_ -> ParenExp x
makeTuple xs = Tuple xs
spanModuleItems xs = do
let
isImport (ModImport _ _ _) = True
isImport _ = False
(imports, items) = Prelude.span isImport xs
forM_ items $ \x -> case x of
ModImport _ start end ->
alexThrow $ \fname ->
emptyError { errorMessage = "all import statements should be at the top of the file."
, errorInlineDesc = Just "unexpected import statement"
, errorFilename = fname
, errorBegin = start
, errorEnd = end
}
_ -> pure ()
pure (map itemImport imports, items)
tupleConSymName :: Int -> T.Text
tupleConSymName n = T.singleton '(' <> T.replicate n (T.singleton ',') <> T.singleton ')'
endOfListPos :: HasPosn x => x -> [x] -> Posn
endOfListPos x [] = endPosn x
endOfListPos _ xs = endPosn (last xs)
}