module Language.Go.Parser.Parser (
goParse,
goParseFileWith,
goParseTestWith,
goTokenize,
goParseTokens,
goSource,
goImportDecl,
goTopLevelDecl,
goType,
goBlock,
goExpression,
goStatement,
) where
import Language.Go.Parser.Operators
import Language.Go.Parser.Tokens
import Language.Go.Parser.Lexer (alexScanTokens)
import Language.Go.Syntax.AST
import Control.Monad
import Data.Maybe (isJust, catMaybes)
import Text.Parsec.Prim hiding (token)
import Text.Parsec.Error (ParseError)
import Text.Parsec.Combinator
goTokenize :: String -> [GoTokenPos]
goTokenize = insertSemi . stripComments . alexScanTokens
goParse :: String -> String -> Either ParseError GoSource
goParse filename s = goParseTokens filename $ goTokenize s
goParseTokens :: String -> [GoTokenPos] -> Either ParseError GoSource
goParseTokens filename toks = runGoParser goSource filename toks
goParseFileWith :: GoParser a -> String -> String -> Either ParseError a
goParseFileWith start filename s = runGoParser start filename (goTokenize s)
goParseTestWith :: GoParser a -> String -> Either ParseError a
goParseTestWith start s = runGoParser start "" (goTokenize s)
goType :: GoParser GoType
goType = goTypeName
<|> goTypeLit
<|> goParen goType
goTypeName :: GoParser GoType
goTypeName = do
(GoQual q n) <- goQualifiedIdent
return $ GoTypeName q n
goTypeLit :: GoParser GoType
goTypeLit = (try goSliceType)
<|> goArrayType
<|> goStructType
<|> goPointerType
<|> goFunctionType
<|> goInterfaceType
<|> goMapType
<|> goChannelType
goArrayType :: GoParser GoType
goArrayType = do
l <- goBracket goExpression
t <- goType
return $ GoArrayType l t
goSliceType :: GoParser GoType
goSliceType = do
goTokLBracket
goTokRBracket
liftM GoSliceType goType
goStructType :: GoParser GoType
goStructType = do
goTokStruct
liftM GoStructType $ goBlockish goFieldDecl
goFieldDecl :: GoParser GoFieldType
goFieldDecl = (try field) <|> anonymous where
parseTag = do
let strlit = token (GoTokStr Nothing "")
GoTokStr (Just lit) s <- try strlit
return $ GoLitStr lit s
field = do
ids <- goIdentifierList
t <- goType
tag <- optionMaybe parseTag
return $ GoFieldType tag ids t
anonymous = do
a <- optionMaybe goTokAsterisk
t <- goTypeName
tag <- optionMaybe parseTag
return $ GoFieldAnon tag (isJust a) t
goPointerType :: GoParser GoType
goPointerType = do
goTokAsterisk
liftM GoPointerType goType
goFunctionType :: GoParser GoType
goFunctionType = do
goTokFunc
liftM GoFunctionType goSignature
goSignature :: GoParser GoSig
goSignature = do
par <- goParameters
res <- option [] goResult
return $ GoSig par res
goResult :: GoParser [GoParam]
goResult = goParameters
<|> do ty <- goType ; return [GoParam [] ty]
goParameters :: GoParser [GoParam]
goParameters = do
goTokLParen
params <- option [] $ do
ps <- goParameterList
optional goTokComma
return ps
goTokRParen
return params
goParameterList :: GoParser [GoParam]
goParameterList = sepBy goParameterDecl goTokComma
goParameterDecl :: GoParser GoParam
goParameterDecl = (try goParameterDecl') <|> goParameterDecl'' where
goParameterDecl' :: GoParser GoParam
goParameterDecl' = do
is <- option [] goIdentifierList
t <- try goVariadicType <|> goType
return $ GoParam is t
goParameterDecl'' :: GoParser GoParam
goParameterDecl'' = do
t <- try goVariadicType <|> goType
return $ GoParam [] t
goInterfaceType :: GoParser GoType
goInterfaceType = do
goTokInterface
xs <- goBlockish goMethodSpec
return $ GoInterfaceType xs
goMethodSpec :: GoParser GoMethSpec
goMethodSpec = try goMethodSpec' <|> goMethodSpec'' where
goMethodSpec'' = do
GoQual pkg id <- goQualifiedIdent
return $ GoIfaceName pkg id
goMethodSpec' = do
n <- goIdentifier
s <- goSignature
return $ GoMethSpec n s
goMapType :: GoParser GoType
goMapType = do
goTokMap
kt <- goBracket goType
et <- goType
return $ GoMapType kt et
goChannelType :: GoParser GoType
goChannelType = do
qi <- goChannelQuip
ty <- goType
return $ GoChannelType qi ty
goChannelQuip :: GoParser GoChanKind
goChannelQuip = do goTokArrow ; goTokChan ; return GoIChan
<|> (try $ do goTokChan ; goTokArrow ; return GoOChan)
<|> (try $ do goTokChan ; return GoIOChan)
goBlock :: GoParser GoBlock
goBlock = do liftM GoBlock $ goBlockish goStatement
goBlockish :: GoParser a -> GoParser [a]
goBlockish p = goBrace $ do
lines <- sepEndBy (optionMaybe p) goTokSemicolon
return $ catMaybes lines
goDeclaration :: GoParser GoDecl
goDeclaration = goConstDecl
<|> goTypeDecl
<|> goVarDecl
goTopLevelDecl :: GoParser GoDecl
goTopLevelDecl = goDeclaration
<|> try goFunctionDecl
<|> try goMethodDecl
goConstDecl :: GoParser GoDecl
goConstDecl = goTokConst >> liftM GoConst (goParenish $ try goConstSpec)
goConstSpec :: GoParser GoCVSpec
goConstSpec = do
id <- goIdentifierList
option (GoCVSpec id Nothing []) (try (goConstSpec' id) <|> goConstSpec'' id) where
goConstSpec' :: [GoId] -> GoParser GoCVSpec
goConstSpec' ids = do
goTokEqual
exs <- goExpressionList
return $ GoCVSpec ids Nothing exs
goConstSpec'' :: [GoId] -> GoParser GoCVSpec
goConstSpec'' ids = do
typ <- goType
goTokEqual
exs <- goExpressionList
return $ GoCVSpec ids (Just typ) exs
goIdentifier :: GoParser GoId
goIdentifier = do
GoTokId name <- token $ GoTokId ""
return $ GoId name
goIdentifierList :: GoParser [GoId]
goIdentifierList = sepBy1 goIdentifier goTokComma
goExpressionList :: GoParser [GoExpr]
goExpressionList = try exprs <|> return []
where exprs = do
x <- goExpression
xs <- many $ try (goTokComma >> goExpression)
return (x:xs)
goTypeDecl :: GoParser GoDecl
goTypeDecl = goTokType >> liftM GoType (goParenish goTypeSpec)
goTypeSpec :: GoParser GoTypeSpec
goTypeSpec = do
id <- goIdentifier
ty <- goType
return $ GoTypeSpec id ty
goVarDecl :: GoParser GoDecl
goVarDecl = goTokVar >> liftM GoVar (goParenish goVarSpec)
goVarSpec :: GoParser GoCVSpec
goVarSpec = do
id <- goIdentifierList
(try (goVarSpec' id) <|> try (goVarSpec'' id) <|> goVarSpec''' id) where
goVarSpec' :: [GoId] -> GoParser GoCVSpec
goVarSpec' ids = do
goTokEqual
exs <- goExpressionList
return $ GoCVSpec ids Nothing exs
goVarSpec'' :: [GoId] -> GoParser GoCVSpec
goVarSpec'' ids = do
typ <- goType
goTokEqual
exs <- goExpressionList
return $ GoCVSpec ids (Just typ) exs
goVarSpec''' :: [GoId] -> GoParser GoCVSpec
goVarSpec''' ids = do
typ <- goType
return $ GoCVSpec ids (Just typ) []
goShortVarDecl :: GoParser GoSimp
goShortVarDecl = do
ids <- goIdentifierList
goTokColonEq
exs <- optionMaybe goExpressionList
case exs of
Just ex -> return (GoSimpVar ids ex)
Nothing -> fail "short var"
goFunctionDecl :: GoParser GoDecl
goFunctionDecl = do
goTokFunc
id <- goIdentifier
sg <- goSignature
bk <- option GoNoBlock goBlock
return $ GoFunc $ GoFuncDecl id sg bk
goMethodDecl :: GoParser GoDecl
goMethodDecl = do
goTokFunc
rc <- goReceiver
id <- goIdentifier
sg <- goSignature
bk <- option GoNoBlock goBlock
return $ GoMeth $ GoMethDecl rc id sg bk
goReceiver :: GoParser GoRec
goReceiver = between goTokLParen goTokRParen (try namedrec <|> anonrec)
where namedrec = do
id <- goIdentifier
pt <- optionMaybe goTokAsterisk
ty <- goTypeName
return $ GoRec (isJust pt) (Just id) ty
anonrec = do
pt <- optionMaybe goTokAsterisk
ty <- goTypeName
return $ GoRec (isJust pt) Nothing ty
goOperand :: GoParser GoPrim
goOperand = (try $ liftM GoLiteral goLiteral)
<|> try goQualifiedIdent
<|> try goMethodExpr
<|> liftM GoParen (goParen goExpression)
goLiteral :: GoParser GoLit
goLiteral = goBasicLit
<|> goCompositeLit
<|> goFunctionLit
<?> "literal"
goBasicLit :: GoParser GoLit
goBasicLit = try $ do
(GoTokenPos _ tok) <- lookAhead anyToken
case tok of
(GoTokInt (Just s) t) -> do anyToken; return $ GoLitInt s t
(GoTokReal (Just s) t) -> do anyToken; return $ GoLitReal s t
(GoTokImag (Just s) t) -> do anyToken; return $ GoLitImag s t
(GoTokChar (Just s) t) -> do anyToken; return $ GoLitChar s t
(GoTokStr (Just s) t) -> do anyToken; return $ GoLitStr s t
x -> fail (show x)
goQualifiedIdent :: GoParser GoPrim
goQualifiedIdent = try qualified <|> liftM (GoQual Nothing) goIdentifier
where qualified = do
qual <- goIdentifier
goTokFullStop
name <- goIdentifier
return $ GoQual (Just qual) name
goCompositeLit :: GoParser GoLit
goCompositeLit = do
st <- getState
ty <- goLiteralType
case ty of
GoTypeName _ _ -> if noComposite st && parenDepth st == 0 then fail "no T{} in condition" else return ()
_ -> return ()
va <- goLiteralValue
return $ GoLitComp ty va
goArrayEllipsisType :: GoParser GoType
goArrayEllipsisType = do
goBracket goTokEllipsis
t <- goType
return $ GoEllipsisType t
goVariadicType :: GoParser GoType
goVariadicType = do
goTokEllipsis
t <- goType
return (GoVariadicType t)
goLiteralType :: GoParser GoType
goLiteralType = (try goArrayType)
<|> (try goArrayEllipsisType)
<|> goSliceType
<|> goStructType
<|> goMapType
<|> goTypeName
goLiteralValue :: GoParser GoComp
goLiteralValue = do
goTokLBrace
elements <- sepEndBy (try goElement) goTokComma
goTokRBrace
return $ GoComp elements
goElement :: GoParser GoElement
goElement = do
key <- option GoKeyNone goKey
val <- goValue
return $ GoElement key val
goKey :: GoParser GoKey
goKey = try fieldcolon
<|> try keycolon
<?> "literal key"
where fieldcolon = do { key <- goIdentifier; goTokColon; return $ GoKeyField key }
keycolon = do { expr <- goExpression; goTokColon; return $ GoKeyIndex expr }
goValue :: GoParser GoValue
goValue = liftM GoValueExpr goExpression
<|> liftM GoValueComp goLiteralValue
<?> "literal value"
goFunctionLit :: GoParser GoLit
goFunctionLit = liftM GoLitFunc goFuncLambda
goFuncLambda :: GoParser GoFuncExpr
goFuncLambda = do
goTokFunc
sg <- goSignature
bk <- goBlock
return $ GoFuncExpr sg bk
goPrimaryExpr :: GoParser GoExpr
goPrimaryExpr = do
ex <- (try goBuiltinCall) <|> (try goOperand) <|> (try goConversion)
let complex prefix = (try $ goIndex prefix)
<|> (try $ goSlice prefix)
<|> try (goTypeAssertion prefix)
<|> goCall prefix
<|> goSelector prefix
let veryComplex prefix = try (do
vex <- complex prefix
veryComplex vex) <|> return prefix
pr <- veryComplex ex
return $ GoPrim pr
goSelector :: GoPrim -> GoParser GoPrim
goSelector ex = do
goTokFullStop
id <- goIdentifier
return $ GoSelect ex id
goIndex :: GoPrim -> GoParser GoPrim
goIndex ex = do
goTokLBracket
enterParen
ix <- goExpression
exitParen
goTokRBracket
return $ GoIndex ex ix
goSlice :: GoPrim -> GoParser GoPrim
goSlice ex = do
goTokLBracket
x <- optionMaybe goExpression
goTokColon
y <- optionMaybe goExpression
goTokRBracket
return $ GoSlice ex x y
goTypeAssertion :: GoPrim -> GoParser GoPrim
goTypeAssertion ex = do
goTokFullStop
goTokLParen
ty <- goType
goTokRParen
return $ GoTA ex ty
goCall :: GoPrim -> GoParser GoPrim
goCall ex = goParen $ do
ar <- goExpressionList
rs <- optionMaybe goTokEllipsis
optional goTokComma
return $ GoCall ex ar (isJust rs)
goExpression :: GoParser GoExpr
goExpression = goOpExpr goUnaryExpr
<?> "expression"
goUnaryExpr :: GoParser GoExpr
goUnaryExpr = unaryExpr
where unaryExpr = try unaryOpExpr <|> goPrimaryExpr
unaryOpExpr = do
op <- goUnaryOp
expr <- unaryExpr
return $ Go1Op op expr
goMethodExpr :: GoParser GoPrim
goMethodExpr = do
rc <- goReceiverType
goTokFullStop
id <- goMethodName
return $ GoMethod rc id
goMethodName = goIdentifier
goReceiverType :: GoParser GoRec
goReceiverType = try goReceiverType' <|> goReceiverType'' where
goReceiverType'' = do
ty <- goParen (goTokAsterisk >> goTypeName)
return $ GoRec True Nothing ty
goReceiverType' = do
ty <- goTypeName
return $ GoRec False Nothing ty
goConversion :: GoParser GoPrim
goConversion = do
ty <- goType
ex <- goParen goExpression
return $ GoCast ty ex
goStatement :: GoParser GoStmt
goStatement = (liftM GoStmtDecl goDeclaration)
<|> try goLabeledStmt
<|> (liftM GoStmtSimple goSimple)
<|> goGoStmt
<|> goReturnStmt
<|> goBreakStmt
<|> goContinueStmt
<|> goGotoStmt
<|> goFallthroughStmt
<|> liftM GoStmtBlock goBlock
<|> goIfStmt
<|> goSwitchStmt
<|> goSelectStmt
<|> goForStmt
<|> goDeferStmt
<?> "statement"
goSimple :: GoParser GoSimp
goSimple = (try goSendStmt)
<|> (try goIncDecStmt)
<|> (try goShortVarDecl)
<|> (try goAssignment)
<|> (liftM GoSimpExpr goExpression)
goLabeledStmt :: GoParser GoStmt
goLabeledStmt = do
id <- goIdentifier
goTokColon
st <- option (GoStmtSimple GoSimpEmpty) goStatement
return $ GoStmtLabeled id st
goSendStmt :: GoParser GoSimp
goSendStmt = do
ch <- goExpression
goTokArrow
ex <- goExpression
return $ GoSimpSend ch ex
goIncDecStmt :: GoParser GoSimp
goIncDecStmt = try $ do
ex <- goUnaryExpr
(GoTokenPos _ op) <- anyToken
case op of
GoTokInc -> return $ GoSimpInc ex
GoTokDec -> return $ GoSimpDec ex
_ -> fail "IncDecStmt What?"
goAssignment :: GoParser GoSimp
goAssignment = do
lv <- goExpressionList
op <- goAssignOp
rv <- goExpressionList
return $ GoSimpAsn lv op rv
goNoComposite :: GoParser a -> GoParser a
goNoComposite p = do
st <- getState
putState $ GoParserState { noComposite = True, parenDepth = 0 }
x <- p
putState st
return x
goCond :: GoParser GoCond
goCond = goNoComposite $ do
st <- optionMaybe (goSemi goSimple)
ex <- optionMaybe goExpression
return $ GoCond st ex
goIfStmt :: GoParser GoStmt
goIfStmt = do
goTokIf
cond <- goCond
case cond of
GoCond _ Nothing -> fail "missing condition in if"
_ -> return ()
b <- goBlock
e <- optionMaybe (goTokElse >> goStatement)
return $ GoStmtIf cond b e
goSwitchStmt :: GoParser GoStmt
goSwitchStmt = try goExprSwitchStmt
<|> goTypeSwitchStmt
goExprSwitchStmt :: GoParser GoStmt
goExprSwitchStmt = do
goTokSwitch
cond <- goCond
cl <- goBrace $ many goExprCaseClause
return $ GoStmtSwitch cond cl
goExprCaseClause :: GoParser (GoCase GoExpr)
goExprCaseClause = do
fn <- goAfter goTokColon goExprSwitchCase
st <- many $ goSemi goStatement
return $ fn st
goExprSwitchCase :: GoParser ([GoStmt] -> GoCase GoExpr)
goExprSwitchCase = goExprSwitchCase' <|> goExprSwitchCase''
where goExprSwitchCase' = do goTokCase; el <- goExpressionList; return $ GoCase el
goExprSwitchCase'' = do goTokDefault; return GoDefault
goTypeSwitchStmt :: GoParser GoStmt
goTypeSwitchStmt = do
goTokSwitch
st <- optionMaybe $ goSemi goSimple
id <- optionMaybe $ goAfter goTokColonEq goIdentifier
ga <- goTypeSwitchGuard st
cl <- goBrace $ many goTypeCaseClause
return $ GoStmtTypeSwitch ga cl id
goTypeSwitchGuard :: (Maybe GoSimp) -> GoParser GoCond
goTypeSwitchGuard st = do
ex <- goExpression
goTokFullStop
goParen goTokType
return $ GoCond st (Just ex)
goTypeCaseClause :: GoParser (GoCase GoType)
goTypeCaseClause = do
fn <- goAfter goTokColon goTypeSwitchCase
st <- many $ goSemi goStatement
return $ fn st
goTypeSwitchCase :: GoParser ([GoStmt] -> GoCase GoType)
goTypeSwitchCase = goTypeSwitchCase' <|> goTypeSwitchCase''
where goTypeSwitchCase' = do goTokCase; tl <- goTypeList; return $ GoCase tl
goTypeSwitchCase'' = do goTokDefault; return GoDefault
goTypeList :: GoParser [GoType]
goTypeList = sepBy1 goType goTokComma
goForStmt :: GoParser GoStmt
goForStmt = do
goTokFor
h <- goNoComposite (try goForClause <|> try goRangeClause <|> goCondition)
b <- goBlock
return $ GoStmtFor h b
goCondition :: GoParser GoForClause
goCondition = liftM GoForWhile (optionMaybe goExpression)
goForClause :: GoParser GoForClause
goForClause = do
i <- option GoSimpEmpty goSimple
goTokSemicolon
c <- optionMaybe goExpression
goTokSemicolon
p <- option GoSimpEmpty goSimple
return $ GoForThree i c p
goRangeClause :: GoParser GoForClause
goRangeClause = do
k <- goExpression
v <- optionMaybe (goTokComma >> goUnaryExpr)
p <- goAnyEqual
goTokRange
e <- goExpression
let lhs = case v of { Nothing -> [k]; Just v -> [k,v] }
return $ GoForRange lhs e (p == GoOp ":=")
goAnyEqual :: GoParser GoOp
goAnyEqual = do goTokEqual; return $ GoOp "="
<|> do goTokColonEq; return $ GoOp ":="
goGoStmt :: GoParser GoStmt
goGoStmt = do goTokGo; liftM GoStmtGo goExpression
goSelectStmt :: GoParser GoStmt
goSelectStmt = do
goTokSelect
cl <- goBrace $ many goCommClause
return $ GoStmtSelect cl
goCommClause :: GoParser (GoCase GoChan)
goCommClause = do
fn <- goAfter goTokColon goCommCase
st <- many $ goSemi goStatement
return $ fn st
goCommCase :: GoParser ([GoStmt] -> GoCase GoChan)
goCommCase = goCommCase' <|> goCommCase''
where goCommCase' = do goTokCase; ch <- goChanStmt; return $ GoCase [ch]
goCommCase'' = do goTokDefault; return GoDefault
goChanStmt :: GoParser GoChan
goChanStmt = try (goSendStmt >>= convert) <|> goRecvStmt
where convert (GoSimpSend x y) = return (GoChanSend x y)
goRecvStmt :: GoParser GoChan
goRecvStmt = do
as <- optionMaybe $ try (do
ex <- goExpression
ex2 <- optionMaybe (goTokComma >> goExpression)
op <- goAnyEqual
return (ex, ex2, op))
re <- goRecvExpr
return $ GoChanRecv as re
goRecvExpr :: GoParser GoExpr
goRecvExpr = try goRecvExpr'
<|> goParen goRecvExpr where
goRecvExpr' = do
goTokArrow
ex <- goExpression
return $ Go1Op (GoOp "<-") ex
goReturnStmt :: GoParser GoStmt
goReturnStmt = do goTokReturn; liftM GoStmtReturn $ option [] goExpressionList
goBreakStmt :: GoParser GoStmt
goBreakStmt = do goTokBreak; liftM GoStmtBreak $ optionMaybe goIdentifier
goContinueStmt :: GoParser GoStmt
goContinueStmt = do goTokContinue; liftM GoStmtContinue $ optionMaybe goIdentifier
goGotoStmt :: GoParser GoStmt
goGotoStmt = do goTokGoto; liftM GoStmtGoto $ goIdentifier
goFallthroughStmt :: GoParser GoStmt
goFallthroughStmt = goTokFallthrough >> return GoStmtFallthrough
goDeferStmt :: GoParser GoStmt
goDeferStmt = goTokDefer >> liftM GoStmtDefer goExpression
goBuiltinCall :: GoParser GoPrim
goBuiltinCall = do
id <- goIdentifier
goTokLParen
tj <- optionMaybe goType
ar <- option [] goBuiltinArgs
goTokRParen
case (id, tj) of
(GoId "new", Just ty) -> return $ GoNew ty
(GoId "make", Just ty) -> return $ GoMake ty ar
_ -> fail "BuiltinCall what?"
goBuiltinArgs :: GoParser [GoExpr]
goBuiltinArgs = goTokComma >> goExpressionList
goSource :: GoParser GoSource
goSource = do
pkg <- goSemi goPackageClause
imp <- many $ goSemi goImportDecl
top <- many $ goSemi goTopLevelDecl
eof
return $ GoSource pkg imp top
goPackageClause :: GoParser GoId
goPackageClause = do
goTokPackage
goIdentifier
goImportDecl :: GoParser GoPrel
goImportDecl = goTokImport >> liftM GoImportDecl goImportSpec
goParenish :: GoParser a -> GoParser [a]
goParenish x = goParen xs <|> one
where one = do { t <- x; return [t] }
xs = do
lines <- sepEndBy (optionMaybe x) goTokSemicolon
return $ catMaybes lines
goImportSpec :: GoParser [GoImpSpec]
goImportSpec = goImpSpecs'' <|> goImpSpecs' where
goImpSpecs' = liftM (replicate 1) goImpSpec
goImpSpecs'' = goParen $ many $ goSemi goImpSpec
goImpSpec :: GoParser GoImpSpec
goImpSpec = do
ty <- try goImpType
ip <- goString
return $ GoImpSpec ty ip
goImpType :: GoParser GoImpType
goImpType = try dot <|> try qual <|> return GoImp
where dot = do { goTokFullStop; return GoImpDot }
qual = do { id <- goIdentifier; return $ GoImpQual id }
goAfter :: GoParser b -> GoParser a -> GoParser a
goAfter y x = try (do z <- x ; y ; return z)
goSemi :: GoParser a -> GoParser a
goSemi = goAfter goTokSemicolon
goString :: GoParser String
goString = do
GoTokStr rep uni <- token (GoTokStr Nothing "")
return uni
goParen :: GoParser a -> GoParser a
goParen p = do
goTokLParen
enterParen
x <- p
exitParen
goTokRParen
return x
goBrace :: GoParser a -> GoParser a
goBrace = between goTokLBrace goTokRBrace
goBracket :: GoParser a -> GoParser a
goBracket = between goTokLBracket goTokRBracket